# Como implementar secure by design em APIs que processam CPF

> Guia completo para implementar o princípio secure by design em APIs que processam dados de CPF, com exemplos de código e boas práticas.

**Publicado:** 17/01/2026
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/implementar-secure-by-design-apis-processam-cpf

---


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](https://owasp.org/www-project-api-security/) 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

```python
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

```bash
# 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.

### Leia também

- [SLA de API de CPF: níveis de disponibilidade e o que exigir do seu provedor](https://cpfhub.io/blog/sla-api-cpf-niveis-disponibilidade)
- [Como configurar proxy reverso para API de CPF em ambientes corporativos](https://cpfhub.io/blog/como-configurar-proxy-reverso-para-api-de-cpf-em-ambientes-corporativos)
- [LGPD: CPF é dado pessoal sensível ou não? Entenda a classificação correta](https://cpfhub.io/blog/lgpd-cpf-e-dado-pessoal-sensivel-ou-nao-entenda-a-classificacao-correta)
- [KYC no Brasil: quais setores são obrigados a validar CPF por lei](https://cpfhub.io/blog/kyc-no-brasil-quais-setores-sao-obrigados-a-validar-cpf-por-lei)

---

## 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**](https://www.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](https://www.cpfhub.io/) e comece a testar com 50 consultas gratuitas por mês, sem cartão de crédito.

