Publicado em 13 de Setembro de 2025
Design patterns são soluções de projeto recorrentes para problemas comuns em engenharia de software. Eles não são frameworks nem trechos de código prontos: são abstrações que guiam decisões de arquitetura e implementação. Ao aplicá-los com intenções claras de segurança, reduzimos ataques decorrentes de más escolhas de design (ex.: exposição desnecessária de superfícies, estados compartilhados inseguros, acoplamento que vaza segredos).
1. O que são Design Patterns
Popularizados pelo Gang of Four (GoF), os padrões se agrupam em três famílias:
- Criação (Factory, Builder, Prototype, Singleton): controlam como objetos são instanciados.
- Estrutural (Adapter, Facade, Proxy, Decorator, Composite, Flyweight, Bridge): organizam composição e acoplamento.
- Comportamental (Strategy, Command, Chain of Responsibility, Observer, Template Method, State, Iterator, Mediator, Memento, Visitor): definem como objetos colaboram e distribuem responsabilidades.
O valor para segurança surge quando escolhemos padrões que limitam superfície de ataque, impõem invariantes e evitam estados perigosos.
2. Padrões GoF com ângulo de segurança — exemplos práticos
- Factory: centraliza a criação de objetos sensíveis (p.ex., clientes de criptografia), impedindo instâncias com parâmetros fracos. Útil para enforce de políticas (algoritmos, tamanhos de chave, sources de entropia).
- Builder: constrói objetos complexos apenas quando todos os campos obrigatórios seguros foram preenchidos; evita “defaults perigosos” (ex.: cipher suites inseguras).
- Singleton: use com parcimônia. Um singleton com estado mutável pode virar hotspot de condição de corrida e vazamento de segredos. Se necessário, preferir Singleton imutável e injeção de dependência.
- Adapter: normaliza entradas externas antes de alcançar o domínio; local ideal para validações e canonicalização (prevenção de injeções e path traversal).
- Facade: reduz superfície de API pública expondo apenas operações seguras; oculta detalhes internos que poderiam ser explorados.
- Proxy: intercepta chamadas para aplicar autorização, rate limiting e políticas de acesso por contexto sem poluir o domínio.
- Decorator: acrescenta auditoria, métricas e sanitização de dados sensíveis sem alterar a classe base.
- Composite: atenção a propagação de privilégios em hierarquias; defina regras de herança de acessos de forma explícita para não “elevar” permissões por engano.
- Bridge: desacopla abstrações de implementações; permite trocar provedores (ex.: KMS) sem reescrever regras de segurança no domínio.
- Strategy: alterna algoritmos de forma segura (p.ex., políticas de hashing); facilita a rotação de algoritmos sem tocar no fluxo de negócio.
- Command: encapsula ações auditáveis com idempotência e autorização por tipo de comando; ótimo para trilhas de auditoria e replay safety.
- Chain of Responsibility: pipeline de validações e filtros (ex.: autenticação → autorização → validação de entrada → verificação de cota). Cada elo falha de forma segura (fail-closed).
- Observer: cuidado com event injection e assinaturas não confiáveis; valide a origem e limite efeitos colaterais.
- Template Method: define passos obrigatórios de um fluxo com ganchos de segurança (ex.: “verificar permissão” antes de “executar operação”).
- State: modela transições de sessão/conta (ex.: locked, verified, pending MFA), evitando ações proibidas em estados inseguro/indefinido.
- Memento: ao versionar estado, não grave segredos em claro; use criptografia e redaction nos snapshots.
- Visitor: útil para aplicar verificações uniformes em estruturas complexas (ex.: varrer uma AST para bloquear chamadas perigosas).
3. Padrões arquiteturais com implicações de segurança
- Layered Architecture (camadas): proíba “saltos” de camada (UI → data); use interfaces e contracts para garantir que validações ocorram na borda correta.
- Hexagonal / Ports & Adapters: coloca entradas/saídas em “portas” explícitas; ótimo ponto único para autenticação, mapeamento e sanitização.
- Microservices: favorece princípio do menor privilégio por serviço; combine com comunicação autenticada (mTLS/assinaturas) e segregação de dados por contexto.
- Event-Driven: defina contratos de evento imutáveis, assine mensagens e valide publishers; evite dados sensíveis em payloads.
- CQRS: separa leitura de escrita; autorizações mais simples e modelos de leitura redigidos para não vazar campos sensíveis.
4. Concorrência e imutabilidade (padrões práticos)
- Immutable Object: objetos de configuração/segredos devem ser imutáveis após criação; evita TOCTOU e condições de corrida.
- Thread Pool com filas: controla consumo de recursos e previne DoS por exaustão interna.
- Copy-on-write para estruturas compartilhadas: reduz risco de corrupção de estado sensível.
5. Anti-patterns que criam vulnerabilidades
- God Object: concentração de responsabilidades e segredos; torna auditoria e controles finos inviáveis.
- Service Locator opaco: mascara dependências, dificulta testes de segurança e policy enforcement.
- Estado global mutável: segredos e flags em
static; origem de corridas e vazamentos. - Serialização insegura: usar formatos que executam gadgets (ex.: object deserialization) — prefira formatos de dados puros (JSON/CBOR) e whitelists de tipos.
- Fail-open: tratamento de erro que libera acesso “para não quebrar”; sempre fail-closed.
- Roll-your-own crypto: abstraia via Factory/Strategy e delegue a bibliotecas maduras.
6. Mini-blueprints (como combinar padrões de forma segura)
- API segura por camadas: Adapter (normaliza entrada) → Chain of Responsibility (auth → authz → validações) → domínio → Decorator (auditoria) → Facade (exposição mínima).
- Gestão de segredos: Factory escolhe provedores; Builder exige parâmetros seguros; objetos de segredo imutáveis; Proxy controla acesso por contexto.
- Fluxo de ações críticas: cada ação como Command com idempotency key, autorização por tipo, trilha de auditoria e Template Method impondo checagens obrigatórias.
- Uploads de arquivo: Facade expõe operação única; Adapter valida mimetype; Sandboxing para processamento; Decorator remove metadados e aplica scan.
7. Boas práticas para adotar padrões com foco em segurança
- Decida o padrão pelo risco: escolha o que melhor protege invariantes de negócio (integridade, confidencialidade, disponibilidade).
- Documente invariantes: ao introduzir um padrão, registre quais propriedades de segurança ele garante e quais trade-offs cria.
- Teste os contratos: escreva testes que quebrem se o padrão for violado (ex.: pular etapa obrigatória do Template Method).
- Evite sobre-engenharia: padrão mal aplicado aumenta complexidade e cria novas brechas.
- Prefira imutabilidade e limites explícitos de acoplamento; são aliados naturais da segurança.
Conclusão
Design patterns existem para resolver problemas de projeto. Quando usados com critério, eles reforçam limites, tornam invariantes auditáveis e reduzem superfícies de ataque. A chave não é “usar muitos padrões”, mas escolher os adequados, aplicá-los de forma consistente e testá-los contra os riscos que o seu sistema realmente enfrenta.