Certificate pinning: quando e como usar com cautela
1. O que é Certificate Pinning e por que existe?
Certificate pinning é a prática de fixar (“prender”) a identidade criptográfica de um servidor — seja seu certificado digital ou sua chave pública — diretamente no cliente que realiza a conexão TLS. Em vez de confiar cegamente em toda a cadeia de Autoridades Certificadoras (CAs), o cliente valida que o certificado apresentado corresponde exatamente ao que foi previamente configurado.
O objetivo principal é mitigar ataques Man-in-the-Middle (MITM), mesmo quando uma CA é comprometida ou quando um atacante consegue emitir um certificado válido para o domínio alvo. Em vez de confiar na hierarquia de CAs, o cliente confia em um certificado ou chave específica.
Existem duas abordagens principais:
- Pinning de certificado: fixa o certificado folha completo (o certificado do servidor). É frágil — qualquer renovação que mude o certificado quebra o pin.
- Pinning de chave pública (SPKI - Subject Public Key Info): fixa apenas a chave pública contida no certificado. Isso permite renovar o certificado (com nova validade, novos SANs etc.) desde que a mesma chave pública seja reutilizada.
A diferença é crucial: com SPKI, você pode renovar o certificado sem quebrar o pin. Com pinning de certificado, qualquer renovação exige atualização do cliente.
2. Cenários onde o pinning é recomendado (e onde não é)
Recomendado
- Aplicações mobile nativas que se comunicam com APIs críticas (bancos, saúde, pagamentos). O app pode ser atualizado via loja, e o controle sobre a infraestrutura é total.
- APIs internas ou fechadas onde você controla tanto o servidor quanto o cliente (ex: microsserviços em uma VPN corporativa).
- Sistemas de alta segurança (fintech, governo, IoT crítico) onde o risco de uma CA comprometida supera o custo operacional do pinning.
Não recomendado
- Sites públicos comuns (blogs, e-commerce não crítico). O custo de manutenção supera o benefício — se o certificado expirar ou for renovado, todos os usuários ficam bloqueados até atualizarem o navegador ou o app.
- Aplicações com rotação frequente de certificados (ex: Let's Encrypt a cada 90 dias). Manter pins atualizados em múltiplos clientes é inviável.
- Cenários com redes corporativas ou proxies (antivírus, firewalls que inspecionam TLS). O pinning quebra essas inspeções legítimas, gerando suporte e insatisfação.
3. Modalidades de pinning: certificado vs. chave pública
| Característica | Pinning de certificado | Pinning de chave pública (SPKI) |
|---|---|---|
| O que é fixado | Certificado folha completo | Apenas a chave pública |
| Tolerância a renovação | Baixa (exige novo certificado idêntico) | Alta (mesma chave, novo certificado) |
| Risco de lockout | Alto (qualquer renovação quebra) | Moderado (só quebra se a chave mudar) |
| Complexidade | Baixa (hash do certificado) | Média (extrair SPKI do certificado) |
Backup pins: a prática recomendada é manter pelo menos dois pins — um primário (atual) e um ou mais de backup (para certificados futuros ou de contingência). Isso evita lockout total se o certificado primário precisar ser trocado antes do previsto.
Exemplo de estratégia: pin da chave pública atual + pin da chave pública do próximo certificado (já provisionado mas ainda não ativo). Se o certificado atual expirar inesperadamente, o backup pin permite a transição.
4. Implementação prática: exemplos de código
Android (Network Security Config)
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.exemplo.com</domain>
<pin-set expiration="2025-12-31">
<pin digest="SHA-256">base64hash_do_certificado_primario=</pin>
<pin digest="SHA-256">base64hash_do_certificado_backup=</pin>
</pin-set>
<!-- Fallback para CA normal se o pin falhar -->
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</domain-config>
</network-security-config>
O atributo expiration no pin-set é essencial: define até quando o pin é válido. Após essa data, o Android ignora o pin e usa a validação normal da CA. Isso permite migração suave sem exigir atualização do app.
iOS (TrustKit)
// Configuração TrustKit (exemplo em Swift)
let trustKitConfig = [
kTSKSwizzleNetworkDelegates: false,
kTSKPinnedDomains: [
"api.exemplo.com": [
kTSKEnforcePinning: true,
kTSKExpirationDate: "2025-12-31",
kTSKPublicKeyHashes: [
"base64hash_da_chave_publica_primaria=",
"base64hash_da_chave_publica_backup="
]
]
]
]
TrustKit.initSharedInstance(withConfiguration: trustKitConfig)
Node.js (axios com custom agent)
const https = require('https');
const axios = require('axios');
const crypto = require('crypto');
const fs = require('fs');
// Hash esperado da chave pública (SPKI)
const EXPECTED_PIN = 'base64hash_da_chave_publica=';
const agent = new https.Agent({
checkServerIdentity: (host, cert) => {
// Extrair SPKI do certificado
const spki = cert.pubkey;
const hash = crypto.createHash('sha256').update(spki).digest('base64');
if (hash !== EXPECTED_PIN) {
return new Error(`Pin mismatch: esperado ${EXPECTED_PIN}, recebido ${hash}`);
}
return undefined; // sucesso
}
});
axios.get('https://api.exemplo.com/dados', { httpsAgent: agent })
.then(res => console.log(res.data))
.catch(err => console.error('Erro de pinning:', err.message));
Navegadores: limitação histórica
O HPKP (HTTP Public Key Pinning) foi deprecado em todos os navegadores modernos devido a abusos e lockouts catastróficos (ex: sites inteiros ficaram inacessíveis por meses). Hoje, navegadores não suportam mais pinning via header HTTP. A alternativa moderna é o Certificate Transparency (CT), que verifica logs públicos em vez de fixar chaves.
5. Armadilhas e riscos do pinning mal feito
- Lockout acidental: o cenário mais temido. Se o certificado expirar e não houver backup pin, todos os clientes com pinning ativo perdem acesso ao serviço. Sem atualização do app, o serviço fica inacessível.
- Falta de fallback: não permitir bypass em redes corporativas (proxy, antivírus que inspecionam TLS) gera suporte e reclamações. O pinning deve ser um reforço, não a única âncora de confiança.
- Complexidade de deploy: atualizar pins exige nova versão do app (app store review). Se o certificado precisar ser trocado com urgência (comprometimento), o tempo de resposta é de dias, não minutos.
- Ataque de downgrade: se o pinning não for aplicado em todas as camadas (ex: HTTP/2 vs HTTP/1.1), um atacante pode forçar o downgrade para HTTP/1.1 sem pinning e contornar a proteção.
6. Boas práticas e estratégias de mitigação
- Sempre usar pinning de chave pública (SPKI) em vez de certificado folha. A flexibilidade para renovar certificados com a mesma chave é essencial.
- Implementar expiration no pin (data de validade). Após essa data, o cliente deve ignorar o pin e usar validação normal da CA. Isso permite transição suave sem exigir atualização do app.
- Usar fallback trust chain (CA normal) + pinning como reforço. Configure o cliente para: primeiro tentar pinning; se falhar, tentar validação normal da CA; se ambos falharem, rejeitar a conexão.
- Monitorar ativamente certificados com ferramentas como
certificate-transparency(logs públicos) ecrt.sh. Isso permite detectar emissões não autorizadas antes que causem dano. - Testar em staging com pinning report-only antes de aplicar em produção. No Android, use
pin-setcomexpirationno passado para testar o comportamento sem bloquear tráfego real.
7. Alternativas modernas ao pinning tradicional
- Certificate Transparency (CT): logs públicos de emissão de certificados. Clientes podem verificar se o certificado foi registrado em logs auditáveis. Detecta CAs maliciosas sem fixar chaves.
- Expect-CT header: forçava o navegador a verificar CT. Foi deprecado em favor de CT embutido nos navegadores.
- TLS 1.3 + 0-RTT: reduz a janela de ataque MITM ao minimizar o handshake e utilizar criptografia mais robusta.
- Certificados de curta duração (short-lived certificates): com Let's Encrypt (90 dias) ou certificados de 24h, a janela de exposição é mínima. Pinning se torna desnecessário — mesmo que um certificado seja comprometido, ele expira rapidamente.
8. Conclusão: usar com cautela, não como dogma
Certificate pinning é uma ferramenta de defesa em profundidade, não uma bala de prata. Em sites públicos, o custo operacional supera o benefício — prefira Certificate Transparency + monitoramento. Em aplicações mobile críticas com ciclo de atualização controlado, o pinning (especialmente SPKI com backup pins e expiration) agrega segurança real.
Checklist para decidir:
- Vale o custo operacional de manter pins atualizados?
- Tenho controle total sobre a renovação de certificados?
- Posso atualizar o cliente rapidamente se algo der errado?
- Existe fallback para redes corporativas (proxy, antivírus)?
Se a resposta for "sim" para todas, o pinning pode ser uma camada extra de segurança. Caso contrário, as alternativas modernas (CT, certificados curtos, TLS 1.3) oferecem proteção similar com muito menos risco operacional.
Lembre-se: segurança não é sobre bloquear tudo, mas sobre equilibrar proteção com usabilidade e resiliência.
Referências
- OWASP Certificate Pinning Cheat Sheet — Guia prático da OWASP com definições, modalidades e exemplos de implementação.
- Android Developers: Network Security Configuration — Documentação oficial do Android sobre configuração de pin-set, expiration e fallback.
- TrustKit GitHub Repository — Biblioteca open-source para iOS/macOS que implementa pinning de chave pública com suporte a relatórios e fallback.
- Let's Encrypt: Short-Lived Certificates — Discussão sobre certificados de curta duração como alternativa ao pinning.
- Certificate Transparency (RFC 6962) — Especificação técnica do Certificate Transparency, a alternativa moderna ao pinning para navegadores.
- crt.sh: Certificate Search — Ferramenta pública para monitorar emissões de certificados e detectar CAs comprometidas.