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.

Redação CPFHub.io
Redação CPFHub.io
··9 min de leitura
Como implementar secure by design em APIs que 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 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.

Redação CPFHub.io

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.

WhatsAppFale conosco via WhatsApp