Princípios de design seguro

TL;DR

Antes de qualquer framework, ferramenta ou protocolo, existe um conjunto de princípios de design que determinam se um sistema pode ser seguro. Saltzer & Schroeder (1975) enumeraram oito; Kerckhoffs (1883) enunciou um nono que sustenta toda a criptografia moderna. Juntos, eles formam o DNA de qualquer sistema bem projetado — e a lista de verificação que todo senior engineer usa implicitamente, mesmo sem saber o nome de cada item. O princípio mais citado em entrevistas é least privilege; o mais frequentemente violado é psychological acceptability; o mais profundo é open design, porque contraria o instinto humano de esconder.


Por que princípios, não receitas

Segurança aplicada está cheia de checklists: OWASP Top 10, CVEs, hardening guides. Úteis, mas insuficientes — um checklist cobre o que já se sabe. Princípios de design operam em nível anterior: eles guiam decisões de arquitetura antes que uma linha de código exista.

A analogia de Bruce Schneier é precisa: receitas de segurança são como antivírus — correm atrás do ataque. Princípios de design são como engenharia estrutural — você não espera o prédio cair para aprender que precisa de fundações.

Os princípios de Saltzer & Schroeder foram publicados em 1975 no artigo “The Protection of Information in Computer Systems” (Proceedings of the IEEE, Vol. 63, No. 9). Cinquenta anos depois, todos os oito continuam válidos sem uma única revisão — o sinal de que são princípios, não hacks.

graph TD
    SS["Saltzer & Schroeder (1975)\n8 Princípios de Design Seguro"]

    P1["1. Economy of mechanism\n(mantenha simples)"]
    P2["2. Fail-safe defaults\n(negue por padrão)"]
    P3["3. Complete mediation\n(verifique TODO acesso)"]
    P4["4. Open design\n(segurança nao depende do segredo)"]
    P5["5. Separation of privilege\n(exija mais de uma condicao)"]
    P6["6. Least privilege\n(minimo necessario)"]
    P7["7. Least common mechanism\n(minimize recursos compartilhados)"]
    P8["8. Psychological acceptability\n(se for hostil, o usuario contorna)"]

    SS --> P1
    SS --> P2
    SS --> P3
    SS --> P4
    SS --> P5
    SS --> P6
    SS --> P7
    SS --> P8

Leitura do diagrama

Os oito princípios não têm ordem de importância — todos se aplicam simultaneamente. Na prática, violações de segurança quase sempre podem ser rastreadas até a quebra de pelo menos um deles. Quando você analisa um CVE ou um post-mortem de incidente, tente identificar qual princípio foi violado: essa leitura transforma casos concretos em aprendizado transferível.


Os oito princípios, um a um

1. Economy of mechanism — mantenha simples

Enunciado original: “Keep the design as simple and small as possible.”

Complexidade é inimiga da segurança. Cada linha de código, cada caso especial, cada feature raramente usada é superfície de ataque potencial. Um mecanismo de proteção simples pode ser analisado, auditado e testado exaustivamente. Um mecanismo complexo não pode — e as interações entre partes que individualmente parecem corretas são exatamente onde as vulnerabilidades se escondem.

É o KISS (“Keep It Simple, Stupid”) aplicado à segurança, com consequências existenciais. OpenSSL tem 500 mil linhas de código; LibreSSL, um fork criado depois do Heartbleed (caso detalhado em Casos práticos), cortou 90 mil linhas e a superfície de ataque encolheu proporcionalmente.

Aplicação prática: APIs com menos endpoints são mais seguras. Configurações com menos opções são mais seguras. Infraestrutura com menos serviços ativos é mais segura. Antes de adicionar uma feature, pergunte: o custo de superfície de ataque vale o benefício?


2. Fail-safe defaults — negue por padrão

Enunciado original: “Base access decisions on permission rather than exclusion.”

O default de qualquer sistema deve ser o estado mais restritivo. A permissão deve ser concedida explicitamente; a negação é o estado de repouso. Em outras palavras: allowlist sempre vence denylist.

A diferença é profunda. Uma denylist enumera o que é proibido — e portanto falha quando aparece algo novo e desconhecido. Uma allowlist enumera o que é permitido — e portanto falha segura quando aparece algo novo: nega por padrão.

flowchart TD
    classDef marca fill:#8855DF33,stroke:#8855DF,color:#E9ECF2
    classDef neutro fill:#1B2029,stroke:#4E5666,color:#C6CCD8
    R["Requisição de acesso"]
    Q{"Está explicitamente\npermitido?"}
    A["Permite"]
    D["Nega"]

    R --> Q
    Q -->|"Sim (allowlist)"| A
    Q -->|"Nao ou desconhecido"| D

    class D neutro
    class A marca

Leitura do diagrama

O fluxo mostra o modelo allowlist: qualquer coisa não explicitamente permitida é negada. Compare com o modelo denylist (inverta as cores): o sistema permite tudo que não está proibido. Quando algo novo e malicioso aparece, o modelo denylist deixa passar; o modelo allowlist bloqueia por padrão.

Fail-safe × fail-secure × fail-open: quando o mecanismo de controle falha (travamento do sistema, erro de rede, timeout), o que acontece? Três comportamentos possíveis:

ComportamentoO que ocorre na falhaExemplo canônico
Fail-safe / fail-secureNega acessoCofre bancário que trava se a eletrônica falhar
Fail-openConcede acessoPorta de emergência que destrava se houver queda de energia
Fail-undefinedComportamento imprevisívelO pior caso — nunca intencional

O contexto determina o correto: a porta de emergência de um prédio em chamas deve ser fail-open (vidas humanas). O cofre de um banco deve ser fail-secure (dinheiro). Uma API de autenticação deve ser fail-secure — se o serviço de auth cair, a resposta correta é negar, não conceder.

O erro clássico de denylist em ferramentas de detecção — WAFs, antivírus por assinatura — está detalhado em Armadilhas comuns.


3. Complete mediation — verifique todo acesso

Enunciado original: “Every access to every object must be checked for authority.”

Não existe acesso pré-aprovado que dure para sempre. Toda vez que um sujeito acessa um objeto — arquivo, registro de banco, endpoint de API — a autorização deve ser verificada naquele momento, contra o estado atual das permissões.

O erro clássico é o cache de decisão de autorização: um sistema verifica a permissão no login, guarda o resultado em sessão, e passa a confiar nele por horas — mesmo que a permissão real tenha mudado nesse meio tempo. O caso do token JWT de 24 horas, detalhado em Casos práticos, é a versão mais comum desse erro em produção.

Complete mediation também é violado quando o objeto muda de estado entre a verificação e o uso — a classe TOCTOU (Time-of-Check to Time-of-Use), detalhada em Armadilhas comuns.

Complete mediation e microserviços

Em arquiteturas de microserviços, complete mediation exige que cada serviço valide o token — não apenas o API gateway. Um gateway que verifica e distribui internamente sem re-verificação cria um ponto único de falha: se um serviço interno for comprometido, ele pode fazer chamadas sem autenticação ao ecossistema inteiro.


4. Open design — o segredo está na chave, não no algoritmo

Enunciado original: “The design should not be secret.”

Este é o princípio mais contra-intuitivo dos oito. O instinto de “se o atacante não sabe como funciona, não pode atacar” é poderoso e quase universalmente errado.

Aqui Saltzer & Schroeder convergem com Auguste Kerckhoffs, que em 1883 enunciou na monografia “La cryptographie militaire”:

“Il faut qu’il n’exige pas le secret, et qu’il puisse sans inconvénient tomber entre les mains de l’ennemi.” (“Um sistema criptográfico não deve exigir sigilo, e deve poder cair nas mãos do inimigo sem causar inconvenientes.“)

A versão moderna — popularizada por Claude Shannon em 1949 — é a máxima de Shannon:

“The enemy knows the system.”

O adversário vai, eventualmente, descobrir como seu sistema funciona. Por reverse engineering, por insider, por vazamento, por publicação acadêmica. Se a segurança depende desse segredo, ela colapsa quando o segredo vaza. E segredos sempre vazam.

Por que algoritmos criptográficos são públicos? AES, RSA, SHA-256, ChaCha20 — todos são completamente especificados e publicados. Isso parece loucura à primeira vista. A razão é que apenas algoritmos públicos podem ser auditados pela comunidade criptográfica global. Algoritmos secretos não podem ser analisados — e a análise criptográfica é o único método confiável de detectar fraquezas.

flowchart LR
    classDef marca fill:#8855DF33,stroke:#8855DF,color:#E9ECF2
    classDef neutro fill:#1B2029,stroke:#4E5666,color:#C6CCD8
    A["Algoritmo secreto\n('security through obscurity')"]
    B["Algoritmo publico\n(open design)"]

    A --> A1["Auditoria impossivel"]
    A --> A2["Fraquezas latentes"]
    A --> A3["Seguranca colapsa\nse algoritmo vaza"]

    B --> B1["Auditoria pela\ncomunidade global"]
    B --> B2["Fraquezas detectadas\ne corrigidas"]
    B --> B3["Seguranca depende\nda chave, nao do algoritmo"]

    class A neutro
    class B marca

Leitura do diagrama

À esquerda, o modelo “security through obscurity”: a segurança depende de manter o algoritmo secreto. À direita, open design: o algoritmo é público e auditado; a segurança repousa inteiramente na chave, que é pequena e pode ser trocada. A diferença prática: trocar uma chave é trivial; redesenhar um algoritmo é anos de trabalho.

Nuance importante: obscuridade como camada adicional não-fundamental é aceitável. Renomear um endpoint de /admin para algo obscuro adiciona fricção para o atacante. Mas nunca como base da segurança — porque a questão não é “se” o atacante descobrirá, é “quando”. A defesa deve funcionar quando o segredo já tiver vazado.

“Security through obscurity” é um antipadrão formalmente reconhecido por normas como o NIST SP 800-123 — ver Armadilhas comuns.


5. Separation of privilege — exija mais de uma condição

Enunciado original: “Where feasible, a protection mechanism that requires two keys to unlock it is more robust and flexible than one that allows access to the presenter of only a single key.”

Nenhuma condição única deve ser suficiente para acesso a recursos críticos. Exigir múltiplas condições independentes significa que comprometer uma delas não é suficiente — o atacante precisa comprometer todas.

Exemplos concretos:

  • Autenticação multi-fator (MFA): senha (algo que você sabe) + OTP (algo que você tem). Comprometer a senha não é suficiente.
  • Dual control em transações bancárias: duas pessoas com chaves físicas diferentes são necessárias para abrir o cofre. Uma única pessoa comprometida ou coagida não é suficiente.
  • Aprovação em quatro olhos (four-eyes principle): deploys em produção exigem aprovação de dois engenheiros. Um engenheiro comprometido não é suficiente.
  • Assinatura de código com HSM: a chave privada fica num Hardware Security Module (HSM) que exige PIN físico + presença no local + autenticação de rede.

Separation of privilege é o fundamento conceitual do MFA — e por isso o princípio é altamente relevante em entrevistas sobre autenticação.

graph TD
    classDef marca fill:#8855DF33,stroke:#8855DF,color:#E9ECF2
    classDef neutro fill:#1B2029,stroke:#4E5666,color:#C6CCD8
    ACCESS["Acesso a recurso critico"]

    C1["Condicao 1\n(fator conhecimento: senha)"]
    C2["Condicao 2\n(fator posse: OTP/token)"]
    C3["Condicao 3 - opcional\n(fator inerencia: biometria)"]

    C1 --> AND{"Todas as condicoes\nsatisfeitas?"}
    C2 --> AND
    C3 --> AND

    AND -->|"Sim"| GRANT["Acesso concedido"]
    AND -->|"Nao (qualquer uma)"| DENY["Acesso negado"]

    class GRANT neutro
    class DENY marca

Leitura do diagrama

A porta AND mostra a essência de separation of privilege: o acesso exige que todas as condições sejam satisfeitas simultaneamente. Comprometer apenas um fator — uma senha vazada, um token roubado — não é suficiente. O atacante precisa comprometer múltiplos canais independentes ao mesmo tempo, o que aumenta exponencialmente o custo do ataque.


6. Least privilege — cada componente com o mínimo necessário

Enunciado original: “Every program and every privileged user of the system should operate using the least amount of privilege necessary to complete the job.”

É o princípio mais citado. A lógica é simples: se um componente é comprometido, o dano é limitado ao que esse componente pode fazer. Least privilege minimiza o blast radius de um comprometimento.

graph LR
    classDef marca fill:#8855DF33,stroke:#8855DF,color:#E9ECF2
    classDef neutro fill:#1B2029,stroke:#4E5666,color:#C6CCD8
    SVC["Servico de pagamento"]

    SVC -->|"Least privilege\n(acesso minimo)"| DB1["Tabela: orders\nSELECT, INSERT"]
    SVC -->|"Sem acesso"| DB2["Tabela: users\n(dados de perfil)"]
    SVC -->|"Sem acesso"| DB3["Tabela: audit_logs\n(imutavel)"]

    SVC2["Conta de servico\ncom root no banco"]
    SVC2 -->|"Acesso total"| DB4["TODOS os dados\nDROP TABLE possivel"]

    class SVC neutro
    class SVC2 marca

Leitura do diagrama

À esquerda, least privilege aplicado: o serviço de pagamento acessa apenas as tabelas necessárias com as operações necessárias. Se o serviço for comprometido, o atacante tem acesso limitado. À direita, a conta de serviço com acesso excessivo: um comprometimento expõe todo o banco, incluindo a possibilidade de destruição de dados.

Aplicações práticas:

  • sudo e contas de serviço: serviços não devem rodar como root. Um servidor web comprometido rodando como www-data com permissões mínimas tem blast radius muito menor do que rodando como root.
  • Escopos de token OAuth2: um token de acesso a fotos não deve poder postar em nome do usuário. Escopos granulares implementam least privilege para tokens.
  • Permissões de banco de dados: a conta de serviço de leitura de relatórios não precisa de INSERT, UPDATE ou DELETE. Conceder apenas SELECT limita o dano se a conta for comprometida.
  • IAM roles em cloud: uma função Lambda que processa imagens não precisa de acesso a RDS. Roles granulares com políticas mínimas são least privilege em prática.
  • Separação de processos: um servidor web deve ser separado do processo que processa pagamentos. Comprometer o servidor web não deve implicar comprometimento do processador de pagamentos.

Least privilege é difícil de manter

O desafio real não é aplicar least privilege na criação — é mantê-lo ao longo do tempo. Permissões tendem a acumular (“permission creep”): começou com SELECT, logo veio uma situação de urgência e alguém adicionou INSERT, depois UPDATE. Auditorias periódicas de permissões são necessárias.


7. Least common mechanism — minimize recursos compartilhados

Enunciado original: “Minimize the amount of mechanism common to more than one user and depended on by all users.”

Recursos compartilhados entre múltiplos usuários ou processos criam canais de comunicação implícitos e superfícies de ataque. Se um mecanismo é compartilhado, comprometer o mecanismo afeta todos os usuários que dele dependem.

Exemplos:

  • Variáveis de ambiente de sistema: um processo malicioso que lê variáveis de ambiente pode extrair segredos de outros processos no mesmo ambiente.
  • Sistemas de arquivos compartilhados: race conditions em arquivos temporários em /tmp são uma classe clássica de vulnerabilidade (TOCTOU novamente).
  • Contêineres vs. VMs: contêineres compartilham o kernel do host; VMs têm kernels separados. Um escape de contêiner pode afetar o host e outros contêineres. VMs têm blast radius menor por design — mais least common mechanism.
  • Cache compartilhado: um cache compartilhado entre usuários pode vazar dados se não implementar namespacing adequado. Cache-poisoning attacks exploram exatamente isso.

O princípio é a base conceitual do isolamento como prática de segurança: processos separados, namespaces separados, instâncias separadas. O custo é overhead de recursos; o benefício é contenção de danos.


8. Psychological acceptability — se for hostil, o usuário contorna

Enunciado original: “It is essential that the human interface be designed for ease of use, so that users routinely and automatically apply the protection mechanisms correctly.”

Este é o princípio humano, e o mais frequentemente negligenciado por engenheiros. A lógica: se um mecanismo de segurança é inconveniente, os usuários encontram formas de contorná-lo. O contorno cria uma brecha que anula o mecanismo.

Senhas complexas obrigatórias com rotação mensal? O usuário escreve a senha num Post-it colado no monitor. VPN obrigatória para trabalhar remotamente mas lenta e com desconexões constantes? O usuário desativa e usa a rede não-protegida. MFA que interrompe o fluxo de trabalho 20 vezes por dia? O usuário pressiona o time-de-segurança até desativar.

A segurança hostil não é mais segura — é menos, porque cria comportamentos de contorno que o sistema de segurança não modela.

Ligação com fator humano (nota 03): Psychological acceptability é o lado de design do fator humano. A nota 03 explora por que humanos são o elo mais fraco; este princípio diz o que os designers de sistemas devem fazer a respeito: tornar o caminho seguro o caminho mais fácil.

Exemplos de psychological acceptability bem-aplicada: autenticação biométrica (impressão digital, Face ID) que é mais rápida do que digitar senha e mais segura; gerenciadores de senha que tornam senhas únicas e longas mais convenientes do que reutilizar uma senha simples; SSO que reduz o número de autenticações sem sacrificar controle.


Kerckhoffs e Shannon — o segredo na chave, não no sistema

O princípio de Kerckhoffs (1883) e a máxima de Shannon (1949) merecem expansão além do item 4.

Kerckhoffs enunciou seis princípios para sistemas criptográficos militares. O mais importante — e o único que sobreviveu como regra universal — é o segundo: o sistema deve ser inquebrável mesmo que tudo sobre ele, exceto a chave, se torne conhecimento público.

Shannon reformulou em linguagem de teoria da informação: “assume the enemy knows the system”. O modelo formal é o adversário de Shannon — um atacante que conhece completamente o algoritmo e busca, apenas com acesso a pares texto-claro/texto-cifrado, recuperar a chave ou o texto-claro.

A implicação prática: toda a segurança de um sistema criptográfico deve residir na chave. A chave é o único componente que precisa permanecer secreto. Isso é vantajoso porque:

  1. A chave é pequena (256 bits para AES, por exemplo) — gerenciar um segredo pequeno é viável.
  2. A chave pode ser trocada sem redesenhar o sistema inteiro.
  3. O algoritmo pode ser auditado pela comunidade global sem comprometer a segurança de instâncias específicas.

Por que isso é contraintuitivo: engenheiros frequentemente adicionam ofuscação ao código como “camada extra de segurança”. Renomear funções, comprimir código, usar strings codificadas em base64. Nada disso adiciona segurança real — um atacante motivado com tempo desmonta o binário em horas. O tempo gasto em ofuscação seria melhor investido em gerenciamento adequado de chaves.

graph LR
    classDef marca fill:#8855DF33,stroke:#8855DF,color:#E9ECF2
    classDef neutro fill:#1B2029,stroke:#4E5666,color:#C6CCD8
    subgraph OBSCURIDADE ["Security through obscurity (ERRADO)"]
        OA["Algoritmo SECRETO"]
        OK["Chave (qualquer tamanho)"]
        OA -->|"Se algoritmo vaza..."| OBREACH["Sistema comprometido\nSem recurso"]
    end

    subgraph KERCKHOFFS ["Principio de Kerckhoffs (CORRETO)"]
        KA["Algoritmo PUBLICO\n(AES, RSA, ChaCha20)"]
        KK["Chave SECRETA\n(256 bits, gerenciada)"]
        KA --> KSAFE["Sistema seguro\nalgo que vaza = troque a chave"]
        KK --> KSAFE
    end

    class OBSCURIDADE neutro
    class KERCKHOFFS marca
    class OBREACH marca
    class KSAFE marca

Leitura do diagrama

O contraste entre os dois modelos é o núcleo do princípio de Kerckhoffs. No modelo errado, o segredo do algoritmo é o único escudo — e quando vaza, não há recurso. No modelo correto, o algoritmo é público e amplamente auditado; o segredo está inteiramente na chave. Se a chave vazou, gire a chave. Se o algoritmo tivesse um flaw, a comunidade criptográfica já teria encontrado — porque tem acesso a ele.


Defense in depth — camadas independentes de defesa

Defense in depth (defesa em profundidade) não é um dos oito princípios de Saltzer & Schroeder, mas é o corolário arquitetural que emerge deles: construa múltiplas camadas de defesa independentes, de modo que a falha de uma camada não resulte em comprometimento total.

O modelo mental clássico é o modelo do queijo suíço de James Reason (originalmente aplicado a acidentes industriais, adotado pela segurança da informação): cada fatia de queijo tem furos em posições aleatórias. Uma fatia sozinha tem furos — mas empilhe várias e a probabilidade de que todos os furos se alinhem é muito baixa. Um incidente só ocorre quando os furos se alinham através de todas as camadas.

flowchart LR
    classDef marca fill:#8855DF33,stroke:#8855DF,color:#E9ECF2
    classDef neutro fill:#1B2029,stroke:#4E5666,color:#C6CCD8
    ATK["Atacante"]

    ATK --> L1["Perimetro de rede\n(firewall, WAF)"]
    L1 --> L2["Autenticacao\n(MFA, zero trust)"]
    L2 --> L3["Autorizacao\n(RBAC, least privilege)"]
    L3 --> L4["Criptografia em transito\n(TLS 1.3)"]
    L4 --> L5["Criptografia em repouso\n(AES-256)"]
    L5 --> L6["Auditoria e deteccao\n(SIEM, alertas)"]
    L6 --> ASSET["Ativo protegido"]

    ATK -.->|"Para chegar ao ativo,\nprecisa vencer TODAS\nas camadas"| ASSET

    class ATK neutro
    class ASSET marca

Leitura do diagrama

Cada caixa é uma camada independente de defesa. O atacante precisa atravessar todas elas para atingir o ativo protegido. A independência é crucial: se uma camada falha, as outras continuam de pé. Note a camada de auditoria no final — mesmo que um atacante chegue perto do ativo, a detecção permite resposta antes do comprometimento total.

Defense in depth na prática:

  • Firewall + autenticação: o firewall bloqueia acesso não-autorizado, mas um atacante que compromete uma credencial válida passa pelo firewall. A autenticação (especialmente MFA) é a segunda camada.
  • TLS + criptografia em repouso: TLS protege dados em trânsito, mas se o servidor for comprometido, dados em repouso não-cifrados ficam expostos. As duas camadas são complementares.
  • Monitoramento e detecção: mesmo que um atacante passe por todas as barreiras, alertas e SIEM permitem detectar e responder antes que o dano seja irreversível.

A relação com zero trust (nota 19) é direta: zero trust é defense in depth levado ao limite, onde nenhuma camada é implicitamente confiável — cada acesso é verificado independentemente, mesmo dentro do perímetro.


Restatements modernos — os princípios com novo vocabulário

Os princípios de Saltzer & Schroeder continuam operando, mas a indústria adotou terminologia que os reenquadra para contextos modernos.

Princípio originalRestatement modernoContexto
Economy of mechanismMinimização de superfície de ataqueCloud security, SAST
Fail-safe defaultsSecure defaults, “secure by default”DevSecOps, frameworks
Open designNo security through obscurityCriptografia, open source
Least privilegeIAM least-privilege, RBAC granularCloud, microserviços
Psychological acceptabilityFriction-aware security, UX de segurançaZero trust, FIDO2
Defense in depthLayered security, blast radius minimizationCloud architecture
Separation of privilegeDual control, MFA, four-eyes principleFintech, DevOps
Complete mediationToken validation por requisicao, stateless authAPIs REST, JWT

"Secure by design" vs "secure by default"

“Secure by design” significa que o sistema foi arquitetado com segurança em mente desde o início (economy of mechanism, complete mediation, etc.). “Secure by default” significa que a configuração padrão — sem qualquer personalização — é a configuração mais segura (fail-safe defaults). Os dois são complementares: você pode ter um sistema com design seguro mas com defaults perigosos (por exemplo, logs verbosos com dados sensíveis ativados por padrão).


Os princípios em relação — leitura integrada

Os oito princípios não são independentes. Eles formam uma teia onde a violação de um frequentemente implica a violação de outro.

Least privilege e least common mechanism se reforçam: minimizar o acesso de cada componente automaticamente minimiza os recursos compartilhados entre eles. Economy of mechanism facilita complete mediation: sistemas simples têm menos pontos de acesso para verificar. Psychological acceptability é o guardião de todos os outros: um princípio de segurança que os usuários contornam deixa de existir na prática.

A leitura sintética: projete sistemas simples (1) com defaults restritivos (2) que verificam todo acesso (3) sem depender de segredo de design (4), exigindo múltiplas condições para acesso crítico (5) com privilégios mínimos (6) e recursos minimamente compartilhados (7), e tornando tudo isso fácil o suficiente para que os usuários não encontrem razão para contornar (8).


Casos práticos

Os oito princípios não vivem só no papel de 1975 — eles explicam, com precisão cirúrgica, por que incidentes reais aconteceram. Dois casos mostram o padrão: uma feature simples demais para justificar sua superfície de ataque, e uma verificação de acesso que parou de acontecer no momento em que mais importava.

Caso 1 — Heartbleed (CVE-2014-0160): economy of mechanism violado

A vulnerabilidade Heartbleed explorou a extensão “heartbeat” do TLS — uma feature de “keep-alive” que poucos usavam e que introduziu um bug de leitura fora dos limites, expondo memória do servidor (incluindo chaves privadas) a qualquer atacante que soubesse pedir. A feature era desnecessária para a maioria dos casos de uso: OpenSSL a mantinha porque “podia ser útil algum dia”, não porque resolvia um problema concreto. Economy of mechanism diria: não adicione o que não precisa — cada linha de código extra é superfície de ataque que alguém, um dia, vai explorar. O TLS é o protocolo que a extensão comprometeu; ver TLS e HTTPS para como o handshake funciona quando está correto.

Caso 2 — token JWT de 24 horas: complete mediation violado

Um sistema emite um token JWT com validade de 24 horas no login. O usuário é demitido às 9h da manhã — mas o token continua criptograficamente válido até a meia-noite, porque a verificação de acesso aconteceu uma única vez, no momento da emissão, e o sistema simplesmente confia nela pelas 24 horas seguintes. Nenhuma etapa volta a perguntar “esse usuário ainda tem permissão?“. É o erro clássico de cache de decisão de autorização, e é exatamente o que complete mediation proíbe: toda vez que um sujeito acessa um objeto, a autorização deve ser verificada naquele momento, contra o estado atual das permissões — não contra um estado congelado horas atrás. A correção é cara mas simples de enunciar: tokens de curta duração (minutos) com refresh, ou revogação ativa checada a cada requisição.

Vídeo — os princípios desta nota, um a um, com desenho na tela

10 Principles for Secure by Design: Baking Security into Your Systems (IBM Technology, ~17 min, EN) percorre praticamente a mesma lista de Saltzer & Schroeder que esta nota trata, e na mesma ordem de raciocínio: privilégio mínimo com segmentação, economia de mecanismo (que ele rebatiza de keep it simple), a recusa da segurança por obscuridade, separação de responsabilidades e padrões seguros de fábrica. A abertura enuncia a tese da nota inteira em uma frase — “the best security is the kind that’s baked in, not bolted on after the fact” — e a definição de defesa em profundidade evita a armadilha de tratá-la como acúmulo de produtos: aos [4:53], “there’s no single mechanism that is providing security; it’s a whole system of interrelated defenses that work together”. O trecho mais valioso para quem projeta é o de aceitabilidade psicológica, o princípio que quase todo material ignora: ele mostra requisitos de senha tão rígidos que empurraram os usuários a contorná-los, e conclui que o time de segurança, ao endurecer o controle, tornou o sistema menos seguro. É o argumento de que usável e seguro não são eixos opostos. O que ele não cobre: Kerckhoffs pelo nome, complete mediation, e o vocabulário formal de Saltzer & Schroeder — ele ensina os princípios, não a genealogia deles.


Armadilhas comuns

O erro clássico de denylist

WAFs e sistemas de detecção baseados em assinaturas são denylist por natureza: bloqueiam o que conhecem. Quando um ataque novo aparecer — e sempre aparece — o sistema não tem resposta. Isso não os torna inúteis, mas significa que não devem ser a única camada de defesa.

TOCTOU — a verificação e o uso não são o mesmo instante

Um usuário acessa um arquivo na abertura: o sistema verifica a permissão e concede. O arquivo é então movido para um diretório restrito. Se o handle (file descriptor) já aberto permanece válido, acessos futuros através dele contornam a verificação — porque ela já aconteceu, num estado do mundo que não existe mais. Essa classe de vulnerabilidade tem nome: TOCTOU (Time-of-Check to Time-of-Use). É a mesma falha estrutural do caso do JWT de 24h, só que no nível do sistema de arquivos em vez do nível de sessão.

"Security through obscurity" é um antipadrão reconhecido

NIST SP 800-123 e outros documentos normativos explicitamente alertam contra depender de obscuridade como controle de segurança primário. Em entrevista, reconhecer esse antipadrão — e saber articular por que — diferencia senior de júnior.


O que vem a seguir

Os oito princípios definem o que é um sistema bem desenhado — mas nenhum deles funciona sem uma peça que esta nota tratou como dada: o segredo. Kerckhoffs disse que a segurança deve residir inteiramente na chave, não no algoritmo; mas de onde vem essa chave? Como um sistema gera um nonce, um token de sessão ou uma chave de 256 bits que um atacante não consiga adivinhar, mesmo conhecendo o algoritmo inteiro? É a pergunta que 05 - Aleatoriedade e segredos resolve: entropia, geradores de números pseudoaleatórios criptográficos versus não-criptográficos, e por que “aleatório o bastante” costuma ser o elo mais fraco escondido atrás de um design impecável — a fundação sobre a qual todo o resto desta nota é construído.

Resumo em uma linha

Os oito princípios de Saltzer & Schroeder (1975) — do qual least privilege e fail-safe defaults são os mais aplicados — e o princípio de Kerckhoffs (“o segredo está na chave, não no algoritmo”) formam o cânone do design seguro: regras atemporais que transformam “tentar não ser hackeado” em engenharia com fundamento.


Em entrevista

Princípios de design seguro aparecem em perguntas de system design (“como você projeta isso com segurança?”) e em perguntas de cultura de engenharia (“como você pensa sobre segurança?”). O vocabulário em inglês é essencial — esses termos são usados diretamente no dia a dia de times internacionais.

Frases prontas:

“The most important principle is least privilege — every component should run with the minimum permissions needed to do its job. This limits the blast radius when something gets compromised.”

“Fail-safe defaults mean that the default state of any access control is ‘deny.’ Permission must be explicitly granted, not assumed. This is why allowlists are always preferable to denylists.”

“Kerckhoffs’s principle says the security of a cryptographic system should rest entirely on the secrecy of the key, not the algorithm. That’s why AES and RSA are fully public — public algorithms get audited; secret algorithms hide their flaws.”

“Defense in depth is about layered, independent defenses. No single layer is assumed to be perfect. If the perimeter fails, authentication stops the attacker. If authentication fails, authorization limits the damage. If authorization fails, monitoring detects and responds.”

“Security through obscurity is not security — it’s a delay. The question isn’t whether the attacker will find out how the system works, it’s when. Your defense must hold after that happens.”

Vocabulário PT → EN:

PortuguêsInglês
Menor privilégioLeast privilege
Padrão seguro / negar por padrãoFail-safe default / deny by default
Lista de permissão / lista de negaçãoAllowlist / denylist
Defesa em profundidadeDefense in depth
Separação de privilégioSeparation of privilege
Mediação completaComplete mediation
Mecanismo mínimo compartilhadoLeast common mechanism
Aceitabilidade psicológicaPsychological acceptability
Segurança pela obscuridadeSecurity through obscurity
Superfície de ataqueAttack surface
Raio de dano / raio de explosãoBlast radius
Controle dual / princípio dos quatro olhosDual control / four-eyes principle
Vazamento de permissõesPermission creep

Fontes

  1. Saltzer, J. H., & Schroeder, M. D. (1975). “The Protection of Information in Computer Systems.” Proceedings of the IEEE, 63(9), 1278–1308. O artigo original — disponível em MIT: https://web.mit.edu/Saltzer/www/publications/protection/
  2. Kerckhoffs, A. (1883). “La cryptographie militaire.” Journal des sciences militaires, 9, 5–38. Tradução parcial em inglês disponível em: https://www.petitcolas.net/kerckhoffs/
  3. Shannon, C. E. (1949). “Communication Theory of Secrecy Systems.” Bell System Technical Journal, 28(4), 656–715. https://ieeexplore.ieee.org/document/6769090
  4. Anderson, R. (2020). Security Engineering: A Guide to Building Dependable Distributed Systems (3rd ed.). Wiley. Capítulo 4 cobre os princípios de Saltzer & Schroeder com exemplos modernos. Disponível parcialmente em: https://www.cl.cam.ac.uk/~rja14/book.html
  5. OWASP. “Security by Design Principles.” OWASP Developer Guide. https://owasp.org/www-project-developer-guide/draft/design/principles/
  6. NIST SP 800-160 Vol. 1 Rev. 1 (2022). “Engineering Trustworthy Secure Systems.” National Institute of Standards and Technology. https://csrc.nist.gov/publications/detail/sp/800-160/vol-1-rev-1/final