Secure by design em APIs que processam CPF significa incorporar autenticação, validação de input, auditoria e headers de segurança desde a primeira linha de código — não como complemento de última hora. A LGPD, em seu artigo 46, torna esse princípio obrigatório para todo agente de tratamento de dados pessoais no Brasil.
Introdução
Secure by design — ou segurança desde a concepção — é um princípio que determina que a segurança deve ser incorporada à arquitetura de um sistema desde o primeiro dia, e não adicionada como camada posterior. Quando se trata de APIs que processam dados de CPF, esse princípio é ainda mais relevante: cada decisão arquitetural, desde a autenticação até o formato de resposta, impacta diretamente a proteção dos dados pessoais.
A LGPD, em seu artigo 46, exige que agentes de tratamento adotem medidas de segurança desde a fase de concepção do produto ou serviço. O OWASP API Security Top 10 documenta os vetores de ataque mais comuns em APIs e serve como referência prática para implementar as camadas de proteção descritas neste guia.
Princípios de secure by design
Minimização da superfície de ataque
Exponha apenas o necessário. Cada endpoint, campo de resposta e método HTTP é um vetor potencial de ataque.
Defesa em profundidade
Implemente múltiplas camadas de proteção — autenticação, autorização, validação, criptografia e monitoramento — para que a falha de uma camada não comprometa os dados.
Princípio do menor privilégio
Cada componente do sistema deve ter apenas as permissões estritamente necessárias para sua função.
Fail secure
Em caso de erro, o sistema deve falhar de forma segura — nunca expondo dados ou concedendo acesso não autorizado.
Separação de responsabilidades
Componentes que lidam com dados de CPF devem ser isolados de outros componentes do sistema.
Arquitetura da API segura
[Cliente] -> [API Gateway / WAF] -> [Rate Limiter]
-> [Autenticação] -> [Autorização] -> [Validação de Input]
-> [Controlador] -> [Serviço de CPF] -> [CPFHub.io API]
-> [Filtro de Resposta] -> [Log de Auditoria] -> [Cliente]
Implementação completa
Estrutura da API com todas as camadas de segurança
import re
import hmac
import hashlib
import secrets
import time
import json
import logging
import requests
from flask import Flask, request, jsonify, g
from functools import wraps
from datetime import datetime, timezone
from collections import defaultdict
app = Flask(__name__)
# Configuração de logging seguro
audit_logger = logging.getLogger("audit")
audit_logger.setLevel(logging.INFO)
handler = logging.FileHandler("api_audit.log")
handler.setFormatter(logging.Formatter("%(message)s"))
audit_logger.addHandler(handler)
# === CAMADA 1: Rate limiting ===
class RateLimiter:
"""Rate limiter para proteger contra abuso."""
def __init__(self, max_requests: int = 50, window_seconds: int = 60):
self.max_requests = max_requests
self.window = window_seconds
self.requests = defaultdict(list)
def is_allowed(self, client_id: str) -> bool:
agora = time.time()
self.requests[client_id] = [
t for t in self.requests[client_id]
if t > agora - self.window
]
if len(self.requests[client_id]) >= self.max_requests:
return False
self.requests[client_id].append(agora)
return True
rate_limiter = RateLimiter()
def rate_limit(func):
"""Decorator de rate limiting."""
@wraps(func)
def wrapper(*args, **kwargs):
client_id = request.headers.get("x-api-key", request.remote_addr)
if not rate_limiter.is_allowed(client_id):
audit_logger.warning(json.dumps({
"evento": "rate_limit_excedido",
"client": client_id[:8] + "...",
"timestamp": datetime.now(timezone.utc).isoformat()
}))
return jsonify({"error": "Rate limit exceeded"}), 429
return func(*args, **kwargs)
return wrapper
# === CAMADA 2: Autenticação ===
API_KEYS = {
"key_abc123": {"nome": "Cliente A", "plano": "pro", "ativo": True},
"key_def456": {"nome": "Cliente B", "plano": "free", "ativo": True}
}
def autenticar(func):
"""Decorator de autenticação por API key."""
@wraps(func)
def wrapper(*args, **kwargs):
api_key = request.headers.get("x-api-key")
if not api_key:
return jsonify({"error": "API key required"}), 401
cliente = API_KEYS.get(api_key)
if not cliente or not cliente["ativo"]:
audit_logger.warning(json.dumps({
"evento": "autenticacao_falha",
"api_key_parcial": api_key[:8] + "...",
"ip": request.remote_addr,
"timestamp": datetime.now(timezone.utc).isoformat()
}))
return jsonify({"error": "Invalid API key"}), 401
g.cliente = cliente
g.api_key_hash = hashlib.sha256(api_key.encode()).hexdigest()[:16]
return func(*args, **kwargs)
return wrapper
# === CAMADA 3: Validação de input ===
def validar_cpf(cpf: str) -> bool:
"""Valida formato e dígitos verificadores do CPF."""
cpf_limpo = re.sub(r"\D", "", cpf)
if len(cpf_limpo) != 11:
return False
if cpf_limpo == cpf_limpo[0] * 11:
return False
# Validar dígitos verificadores
for i in range(9, 11):
soma = sum(int(cpf_limpo[j]) * ((i + 1) - j) for j in range(i))
resto = soma % 11
digito = 0 if resto < 2 else 11 - resto
if int(cpf_limpo[i]) != digito:
return False
return True
def validar_input(func):
"""Decorator de validação de input."""
@wraps(func)
def wrapper(cpf, *args, **kwargs):
# Sanitizar input
cpf_limpo = re.sub(r"\D", "", cpf)
# Validar formato
if not validar_cpf(cpf_limpo):
return jsonify({"error": "Invalid CPF format"}), 400
# Verificar injection patterns
if re.search(r"[<>'\";]", cpf):
audit_logger.warning(json.dumps({
"evento": "input_suspeito",
"ip": request.remote_addr,
"input": cpf[:20],
"timestamp": datetime.now(timezone.utc).isoformat()
}))
return jsonify({"error": "Invalid input"}), 400
return func(cpf_limpo, *args, **kwargs)
return wrapper
# === CAMADA 4: Auditoria ===
def auditar(func):
"""Decorator de auditoria completa."""
@wraps(func)
def wrapper(*args, **kwargs):
inicio = time.time()
resultado = func(*args, **kwargs)
duracao = (time.time() - inicio) * 1000
cpf_arg = kwargs.get("cpf", args[0] if args else "")
cpf_masked = f"***{cpf_arg[3:6]}***" if len(cpf_arg) >= 6 else "***"
log_entry = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"cliente": g.api_key_hash if hasattr(g, "api_key_hash") else "unknown",
"endpoint": request.path,
"metodo": request.method,
"cpf_masked": cpf_masked,
"status": resultado[1] if isinstance(resultado, tuple) else 200,
"duracao_ms": round(duracao, 2),
"ip": request.remote_addr,
"user_agent": request.headers.get("User-Agent", "")[:100]
}
audit_logger.info(json.dumps(log_entry))
return resultado
return wrapper
# === CAMADA 5: Filtro de resposta ===
def filtrar_resposta(func):
"""Remove campos sensíveis da resposta conforme configuração."""
@wraps(func)
def wrapper(*args, **kwargs):
resultado = func(*args, **kwargs)
if isinstance(resultado, tuple):
dados, status = resultado
else:
dados, status = resultado, 200
# Remover metadados internos
if isinstance(dados, dict):
dados.pop("_internal", None)
# Adicionar headers de segurança
response = jsonify(dados)
response.status_code = status
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["Cache-Control"] = "no-store, no-cache, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
return response
return resultado
return wrapper
# === Endpoint principal ===
@app.route("/api/v1/cpf/<cpf>", methods=["GET"])
@rate_limit
@autenticar
@validar_input
@auditar
@filtrar_resposta
def consultar_cpf(cpf: str):
"""Endpoint seguro para consulta de CPF."""
try:
response = requests.get(
f"https://api.cpfhub.io/cpf/{cpf}",
headers={
"x-api-key": "SUA_API_KEY_CPFHUB",
"Accept": "application/json"
},
timeout=30
)
if response.status_code == 200:
return response.json(), 200
elif response.status_code == 404:
return {"error": "CPF not found"}, 404
else:
return {"error": "Service unavailable"}, 503
except requests.exceptions.Timeout:
return {"error": "Service timeout"}, 504
except requests.exceptions.ConnectionError:
return {"error": "Service unavailable"}, 503
# === Handler de erros seguro ===
@app.errorhandler(Exception)
def handle_error(error):
"""Handler global que nunca expõe detalhes internos."""
audit_logger.error(json.dumps({
"evento": "erro_interno",
"tipo": type(error).__name__,
"timestamp": datetime.now(timezone.utc).isoformat()
}))
return jsonify({"error": "Internal server error"}), 500
if __name__ == "__main__":
app.run(host="0.0.0.0", port=443, ssl_context="adhoc")
Teste completo via cURL
# Consulta autenticada
curl -X GET "https://api.cpfhub.io/cpf/12345678901" \
-H "x-api-key: SUA_API_KEY" \
-H "Accept: application/json" \
--max-time 30
# Verificar headers de segurança
curl -X GET "https://api.cpfhub.io/cpf/12345678901" \
-H "x-api-key: SUA_API_KEY" \
-H "Accept: application/json" \
--max-time 30 \
-v 2>&1 | grep -i "x-content-type\|x-frame\|strict-transport"
Checklist de secure by design
Autenticação e autorização
- API keys rotacionadas periodicamente.
- Autenticação obrigatória em todos os endpoints.
- Autorização baseada em papéis (RBAC).
- Tokens com expiração curta.
Validação de input
- Sanitização de todos os parâmetros.
- Validação de formato de CPF com dígitos verificadores.
- Proteção contra injection (SQL, XSS, SSRF).
- Limitação de tamanho de input.
Proteção de dados
- TLS 1.2+ obrigatório.
- Headers de segurança em todas as respostas.
- CPF nunca em URLs de log ou mensagens de erro.
- Cache desabilitado para respostas com dados pessoais.
Resiliência
- Rate limiting por cliente e por IP.
- Circuit breaker para dependências externas.
- Timeout configurado em todas as chamadas externas.
- Graceful degradation em caso de falha.
Monitoramento
- Log de auditoria para toda operação.
- Alertas para padrões anômalos.
- Métricas de segurança em tempo real.
- Revisão periódica de logs.
Testes de segurança
Inclua no seu pipeline de CI/CD:
- SAST (Static Application Security Testing): análise estática do código.
- DAST (Dynamic Application Security Testing): testes em runtime.
- Dependency scanning: verificação de vulnerabilidades em dependências.
- Penetration testing: testes de penetração periódicos.
- Fuzzing: testes com inputs aleatórios para detectar falhas.
Perguntas frequentes
Por que o CPF não deve aparecer em URLs de log?
URLs de requisição ficam armazenadas em logs de servidores, proxies, CDNs e ferramentas de APM — ambientes com controle de acesso geralmente mais frouxo que os bancos de dados da aplicação. Se o CPF estiver na URL (/cpf/12345678900), ele aparece nesses logs em texto claro. A prática recomendada é receber o CPF via path parameter apenas internamente e mascarar (***345***) em qualquer registro de auditoria.
Como implementar fail secure sem impactar a experiência do usuário?
Fail secure significa que erros inesperados resultam em negação de acesso, nunca em permissão silenciosa. Na prática, o handler global de erros retorna HTTP 500 com mensagem genérica (Internal server error) sem detalhes internos, e o log de auditoria registra o tipo do erro para investigação posterior. O usuário recebe uma mensagem de "serviço temporariamente indisponível" e pode tentar novamente — sem exposição de dados ou bypass de autenticação.
O rate limiting da minha API interna afeta o limite do plano CPFHub.io?
São dois controles independentes. O rate limiter da sua API (camada 1 no código) protege seu próprio endpoint contra abuso. O limite do plano CPFHub.io (50 consultas grátis/mês) é contabilizado no lado da CPFHub.io por API key. Ao atingir o limite do plano gratuito, a CPFHub.io não bloqueia — cobra R$0,15 por consulta adicional e continua respondendo normalmente.
Quais headers de segurança são obrigatórios para APIs que processam CPF?
Para compliance com LGPD e boas práticas de segurança: Cache-Control: no-store (impede cache de dados pessoais), Strict-Transport-Security (força HTTPS), X-Content-Type-Options: nosniff (previne MIME sniffing) e X-Frame-Options: DENY (bloqueia clickjacking). O Content-Security-Policy é relevante se a API também serve HTML. Todos estão implementados no decorator filtrar_resposta do exemplo acima.
Conclusão
Secure by design não é um recurso opcional — é um requisito fundamental para APIs que processam dados de CPF. Ao incorporar segurança em cada camada da arquitetura, desde a autenticação até o formato de resposta, você constrói um sistema resiliente que protege os dados pessoais de forma consistente. A segurança não deve ser a última camada adicionada antes do deploy, mas a primeira consideração no design.
Para integrar consultas de CPF em APIs construídas com secure by design, utilize um provedor que compartilhe os mesmos padrões de segurança. A CPFHub.io responde em ~900ms, exige autenticação por API key em todas as requisições e não armazena os CPFs consultados além do necessário para o processamento. Cadastre-se em cpfhub.io e comece a testar com 50 consultas gratuitas por mês, sem cartão de crédito.
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



