Criptografia simétrica: AES na prática
1. Fundamentos do AES para Desenvolvedores
AES (Advanced Encryption Standard) é o algoritmo de criptografia simétrica mais utilizado no mundo. "Simétrica" significa que a mesma chave é usada tanto para cifrar quanto para decifrar os dados. Isso difere da criptografia assimétrica (como RSA), onde existem chaves pública e privada distintas.
O AES opera em blocos de 128 bits (16 bytes) e oferece três modos de operação principais que todo desenvolvedor precisa conhecer:
- ECB (Electronic Codebook): o modo mais simples e perigoso. Cada bloco é cifrado independentemente, o que faz com que padrões iguais nos dados gerem blocos cifrados idênticos — um vazamento grave de informação.
- CBC (Cipher Block Chaining): cada bloco é combinado com o bloco cifrado anterior antes de ser cifrado. Exige um vetor de inicialização (IV) e padding (geralmente PKCS#7).
- GCM (Galois/Counter Mode): modo autenticado que combina cifragem e verificação de integridade em um único passo. É o mais recomendado para a maioria dos casos.
Por que AES é o padrão atual? Segurança comprovada por décadas de análise criptográfica, desempenho excelente em hardware e software, e suporte nativo em praticamente todas as linguagens modernas.
2. Tamanhos de Chave e Implicações de Segurança
AES suporta três tamanhos de chave:
- AES-128: 128 bits (16 bytes). Seguro para uso geral, com desempenho ligeiramente superior.
- AES-192: 192 bits (24 bytes). Raramente usado na prática.
- AES-256: 256 bits (32 bytes). Oferece margem de segurança contra ataques quânticos futuros. Recomendado para dados altamente sensíveis.
Recomendação prática: use AES-256-GCM como padrão. Se o desempenho for crítico e os dados não forem extremamente sensíveis, AES-128-GCM é aceitável.
A geração de chaves deve usar fontes criptograficamente seguras. Exemplo em Python:
import os
# Gerar chave AES-256 (32 bytes)
chave = os.urandom(32)
# Gerar IV para GCM (12 bytes é o tamanho recomendado)
iv = os.urandom(12)
Nunca derive chaves de senhas sem um KDF (Key Derivation Function) como PBKDF2 ou Argon2.
3. Modos de Operação e Seus Riscos
ECB — O Modo Proibido
O ECB cifra blocos idênticos em cifras idênticas. Isso permite que um atacante identifique padrões no texto cifrado.
# EXEMPLO DO PROBLEMA — NÃO USE ECB
# Se cifrarmos "AAAA" e "BBBB" com ECB:
# "AAAA" -> cifra X
# "BBBB" -> cifra Y
# Mas se o texto tiver "AAAA" em duas posições, ambas geram cifra X
CBC — Exige Cuidados
O CBC requer um IV aleatório e único para cada operação. O IV não precisa ser secreto, mas nunca deve ser reutilizado com a mesma chave.
# CBC exige padding PKCS#7
# Se o último bloco tiver 5 bytes, adiciona-se 11 bytes de valor 0x0B
# Isso torna o tamanho do texto cifrado sempre múltiplo de 16
GCM — O Mais Seguro
GCM fornece autenticação integrada. Você obtém o texto cifrado e um tag de autenticação que verifica se os dados não foram adulterados.
# GCM produz:
# - texto cifrado (mesmo tamanho do original)
# - tag de autenticação (16 bytes)
# - usa nonce (outro nome para IV) de 12 bytes
4. Gerenciamento de Chaves e IVs na Prática
Armazenamento Seguro de Chaves
Nunca hardcode chaves no código-fonte. Use:
- Variáveis de ambiente: para ambientes de desenvolvimento e produção simples.
- Cofres de segredos: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault.
- HSM (Hardware Security Module): para requisitos de segurança máxima.
IVs — Regra de Ouro
Nunca reutilize o mesmo IV com a mesma chave. Gere um novo IV aleatório para cada operação de cifragem. O IV pode ser armazenado junto com o texto cifrado (em texto claro).
# Estrutura de armazenamento recomendada:
# [IV (12 bytes)] + [texto cifrado] + [tag (16 bytes)]
Rotação de Chaves
Implemente rotação periódica de chaves. Estratégia comum: manter uma chave ativa para novas cifragens e uma ou mais chaves antigas para decifragem de dados existentes.
5. Implementação Segura com Bibliotecas Comuns
Exemplo em Python com cryptography
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os
def cifrar(dados: bytes, chave: bytes) -> bytes:
aesgcm = AESGCM(chave)
iv = os.urandom(12)
# GCM cifra e autentica em um único passo
texto_cifrado = aesgcm.encrypt(iv, dados, None)
# Retorna IV + texto_cifrado (que inclui o tag)
return iv + texto_cifrado
def decifrar(dados_cifrados: bytes, chave: bytes) -> bytes:
aesgcm = AESGCM(chave)
iv = dados_cifrados[:12]
texto_cifrado = dados_cifrados[12:]
return aesgcm.decrypt(iv, texto_cifrado, None)
# Uso
chave = os.urandom(32)
dados = b"Mensagem secreta"
cifrado = cifrar(dados, chave)
decifrado = decifrar(cifrado, chave)
print(decifrado.decode()) # Mensagem secreta
Exemplo em Node.js com crypto
const crypto = require('crypto');
function cifrar(texto, chave) {
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', chave, iv);
let cifrado = cipher.update(texto, 'utf8', 'hex');
cifrado += cipher.final('hex');
const tag = cipher.getAuthTag().toString('hex');
return iv.toString('hex') + ':' + cifrado + ':' + tag;
}
function decifrar(pacote, chave) {
const partes = pacote.split(':');
const iv = Buffer.from(partes[0], 'hex');
const cifrado = partes[1];
const tag = Buffer.from(partes[2], 'hex');
const decipher = crypto.createDecipheriv('aes-256-gcm', chave, iv);
decipher.setAuthTag(tag);
let texto = decipher.update(cifrado, 'hex', 'utf8');
texto += decipher.final('utf8');
return texto;
}
// Uso
const chave = crypto.randomBytes(32);
const cifrado = cifrar("Mensagem secreta", chave);
const decifrado = decifrar(cifrado, chave);
console.log(decifrado); // Mensagem secreta
Cuidado fundamental: nunca implemente AES manualmente. Sempre use bibliotecas testadas e auditadas.
6. Armadilhas Comuns e Como Evitá-las
Chaves Fixas ou Derivadas de Senhas Fracas
# ERRADO: chave derivada diretamente de senha
senha = "minha_senha"
chave = senha.encode()[:32] # Péssima ideia!
# CERTO: usar KDF com salt
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
salt = os.urandom(16)
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=salt, iterations=600000)
chave = kdf.derive(senha.encode())
Esquecer a Autenticação
Cifrar sem autenticação permite que um atacante modifique o texto cifrado. CBC + HMAC funciona, mas GCM já resolve isso.
Vazamento de Metadados
O tamanho do texto cifrado revela o tamanho aproximado do original. Para dados muito sensíveis, considere padding para tamanhos fixos.
7. Casos de Uso no Desenvolvimento Web
Criptografia de Dados em Repouso
Cifre tokens de sessão, dados pessoais e informações financeiras antes de armazenar no banco de dados ou Redis:
# Exemplo: cifrar payload JSON antes de salvar no Redis
import json
payload = {"user_id": 123, "email": "usuario@exemplo.com"}
payload_bytes = json.dumps(payload).encode()
payload_cifrado = cifrar(payload_bytes, chave)
redis.set("sessao:123", payload_cifrado)
Criptografia em Trânsito (Camada de Aplicação)
O TLS protege os dados durante o transporte, mas a criptografia em nível de aplicação oferece proteção adicional caso o TLS seja comprometido ou os logs sejam expostos.
Exemplo Prático: API com Dados Cifrados
# POST /api/dados-sensiveis
# 1. Recebe dados do cliente via HTTPS
# 2. Cifra com AES-256-GCM
# 3. Armazena no banco (IV + cifrado + tag)
# 4. Decifra apenas quando necessário, em ambiente controlado
Referências
- Documentação oficial da biblioteca cryptography (Python) — Guia completo sobre cifras simétricas, incluindo AES-GCM com exemplos práticos
- Node.js crypto documentation - Cipher — Documentação oficial do módulo crypto para AES em Node.js, com modos CBC e GCM
- NIST SP 800-38D: Recommendation for Block Cipher Modes of Operation - GCM — Especificação oficial do NIST sobre o modo GCM, incluindo requisitos de IV e segurança
- OWASP Cryptographic Storage Cheat Sheet — Guia prático da OWASP sobre armazenamento criptográfico seguro, com recomendações de algoritmos e gerenciamento de chaves
- Practical Cryptography with Go: AES-GCM — Explicação detalhada de AES-GCM com exemplos em Go, abordando nonce, tag e boas práticas
- CryptoStack - AES Encryption — Tutorial interativo sobre os modos de operação do AES, com visualizações que mostram os riscos do ECB
- Latacora - The Defaults for Crypto — Artigo técnico recomendando AES-256-GCM como padrão e explicando por que outros modos são problemáticos