Logo

Segurança e Hardening

Guia completo de segurança, autenticação e proteção do Sinapse

Este guia detalha todas as camadas de segurança implementadas no Sinapse e as melhores práticas para manter o sistema protegido.

Visão Geral de Segurança

O Sinapse implementa múltiplas camadas de segurança para proteger dados sensíveis de saúde:

Carregando diagrama...

Inicialização Segura do Sistema

Sistema Master Key

A master_key é uma chave especial para criar o primeiro administrador:

IMPORTANTE: A master key funciona APENAS uma vez - para criar o primeiro admin. Após isso, é permanentemente bloqueada.

Configuração

# .env
MASTER_KEY=sua_chave_super_secreta_aqui_min_32_chars

Fluxo de Inicialização

Carregando diagrama...

Métodos Alternativos de Inicialização

# Cria admin automaticamente na inicialização
INIT_ADMIN_EMAIL=[email protected]
INIT_ADMIN_PASSWORD=SenhaForte123!
INIT_ADMIN_NAME=Administrador
# Token gerado nos logs se não houver admin
docker logs sinapse_api | grep "Token de inicialização"

# Use em /api/v1/health_system/system/initialize
curl -X POST http://localhost:8000/api/v1/health_system/system/initialize \
  -H "Content-Type: application/json" \
  -d '{
    "token": "TOKEN_DOS_LOGS",
    "admin_email": "[email protected]",
    "admin_password": "SenhaForte123!",
    "admin_name": "Administrador"
  }'
# Apenas se não houver admin
curl -X POST http://localhost:8000/api/v1/usuarios/signup \
  -H "Content-Type: application/json" \
  -d '{
    "master_key": "sua_master_key",
    "email": "[email protected]",
    "senha": "SenhaForte123!",
    "nome": "Administrador"
  }'

Autenticação JWT

Configuração de Tokens

# Segurança JWT
JWT_SECRET_KEY=chave_secreta_diferente_da_secret_key_min_32_chars
JWT_ALGORITHM=HS256
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=60
JWT_REFRESH_TOKEN_EXPIRE_DAYS=7

# Para produção, considere RS256
JWT_ALGORITHM=RS256
JWT_PRIVATE_KEY_PATH=/path/to/private.pem
JWT_PUBLIC_KEY_PATH=/path/to/public.pem

Estrutura do Token

{
  "sub": "user_id",
  "type": "access|refresh",
  "exp": 1640995200,
  "iat": 1640991600,
  "jti": "unique-token-id",
  "permissions": ["agravos.listar", "agravos.criar"]
}

Implementação Segura

# core/security.py
from datetime import datetime, timedelta, timezone
from jose import JWTError, jwt
import secrets

class TokenService:
    def create_access_token(self, user_id: int) -> str:
        expire = datetime.now(timezone.utc) + timedelta(
            minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES
        )
        
        to_encode = {
            "sub": str(user_id),
            "type": "access",
            "exp": expire,
            "iat": datetime.now(timezone.utc),
            "jti": secrets.token_urlsafe(16)
        }
        
        return jwt.encode(
            to_encode, 
            settings.JWT_SECRET_KEY, 
            algorithm=settings.JWT_ALGORITHM
        )

Sistema RBAC (Role-Based Access Control)

Hierarquia de Permissões

Carregando diagrama...

Grupos Padrão e Permissões

GrupoDescriçãoPermissões Principais
adminAdministrador totaladmin (acesso total)
gestorGestor de saúdeusuarios.*, grupos.*, agravos.*
analistaAnalista de dados*.listar, agravos.estatisticas
usuarioUsuário padrãoagravos.listar, agravos.criar
visualizadorApenas leitura*.listar

Decoradores de Segurança

from fastapi import Depends
from core.permissions import requer_permissao, PermissoesBase

# Endpoint protegido por permissão
@router.post("/agravos/casos")
async def criar_caso(
    caso: CasoSchema,
    current_user = Depends(requer_permissao(PermissoesBase.AGRAVOS_CRIAR))
):
    # Usuário tem permissão garantida
    return await criar_novo_caso(caso, current_user)

# Verificação programática
from core.permissions import verificar_permissao

async def processar_dados(usuario: UsuarioModel, dados: dict):
    if not verificar_permissao(usuario, PermissoesBase.ADMIN):
        raise HTTPException(403, "Operação requer permissão de admin")
    
    # Processar dados sensíveis

Segurança de Senha

Requisitos Obrigatórios

Mínimo 8 caracteres Pelo menos 1 letra maiúscula (A-Z) Pelo menos 1 letra minúscula (a-z) Pelo menos 1 número (0-9) Pelo menos 1 caractere especial (!@#$%^&*)

Validação e Hash

from passlib.context import CryptContext
import re

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def validar_senha(senha: str) -> List[str]:
    erros = []
    
    if len(senha) < 8:
        erros.append("Senha deve ter no mínimo 8 caracteres")
    if not re.search(r"[A-Z]", senha):
        erros.append("Senha deve conter pelo menos 1 letra maiúscula")
    if not re.search(r"[a-z]", senha):
        erros.append("Senha deve conter pelo menos 1 letra minúscula")
    if not re.search(r"\d", senha):
        erros.append("Senha deve conter pelo menos 1 número")
    if not re.search(r"[!@#$%^&*(),.?\":{}|<>]", senha):
        erros.append("Senha deve conter pelo menos 1 caractere especial")
    
    return erros

def hash_senha(senha: str) -> str:
    return pwd_context.hash(senha)

def verificar_senha(senha_plana: str, senha_hash: str) -> bool:
    return pwd_context.verify(senha_plana, senha_hash)

Rate Limiting e Proteção DDoS

Configuração

# Rate Limiting
SECURITY_MAX_LOGIN_ATTEMPTS=5
SECURITY_LOCKOUT_DURATION=900  # 15 minutos
SECURITY_PROGRESSIVE_DELAY=true
SECURITY_MAX_IP_ATTEMPTS_HOUR=20
SECURITY_IP_BLOCK_DURATION=3600  # 1 hora

# API Rate Limits
RATE_LIMIT_PER_MINUTE=60
RATE_LIMIT_PER_HOUR=1000
RATE_LIMIT_BURST=10

Implementação com Redis

from core.cache import redis_client
import time

class RateLimiter:
    async def check_rate_limit(
        self, 
        key: str, 
        limit: int, 
        window: int
    ) -> bool:
        current = int(time.time())
        window_start = current - window
        
        # Remove entradas antigas
        await redis_client.zremrangebyscore(key, 0, window_start)
        
        # Conta requisições na janela
        count = await redis_client.zcard(key)
        
        if count >= limit:
            return False
        
        # Adiciona nova requisição
        await redis_client.zadd(key, {str(current): current})
        await redis_client.expire(key, window)
        
        return True

# Middleware de rate limiting
@app.middleware("http")
async def rate_limit_middleware(request: Request, call_next):
    client_ip = request.client.host
    key = f"rate_limit:{client_ip}"
    
    limiter = RateLimiter()
    if not await limiter.check_rate_limit(key, 60, 60):
        return JSONResponse(
            status_code=429,
            content={"error": "Too Many Requests"},
            headers={"Retry-After": "60"}
        )
    
    return await call_next(request)

Auditoria e Logs

Eventos Auditados

  • Login/logout (sucesso e falha)
  • Criação/edição/exclusão de recursos
  • Alterações de permissões
  • Acessos negados
  • Uso de master_key
  • Exportação de dados

Formato de Log Estruturado

import structlog
from datetime import datetime

logger = structlog.get_logger()

async def log_security_event(
    event_type: str,
    user_id: Optional[int],
    details: dict,
    request: Request
):
    await logger.ainfo(
        "security_event",
        event_type=event_type,
        timestamp=datetime.utcnow().isoformat(),
        user_id=user_id,
        ip_address=request.client.host,
        user_agent=request.headers.get("user-agent"),
        method=request.method,
        path=request.url.path,
        details=details
    )

# Exemplo de uso
await log_security_event(
    event_type="permission_denied",
    user_id=current_user.id,
    details={
        "required_permission": "agravos.deletar",
        "user_permissions": current_user.permissoes
    },
    request=request
)

Retenção de Logs

# Política de retenção
log_retention:
  security_events: 365d  # 1 ano
  access_logs: 90d       # 3 meses
  error_logs: 180d       # 6 meses
  debug_logs: 7d         # 1 semana

Proteção de Endpoints

Headers de Segurança

from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware

# CORS configurado restritivamente
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,  # Lista específica
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["*"],
    max_age=3600,
)

# Trusted hosts
app.add_middleware(
    TrustedHostMiddleware,
    allowed_hosts=settings.ALLOWED_HOSTS
)

# Security headers
@app.middleware("http")
async def security_headers(request: Request, call_next):
    response = await call_next(request)
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["X-Frame-Options"] = "DENY"
    response.headers["X-XSS-Protection"] = "1; mode=block"
    response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
    response.headers["Content-Security-Policy"] = "default-src 'self'"
    return response

Validação de Entrada

from pydantic import BaseModel, validator, EmailStr
from typing import Optional
import re

class UsuarioCreate(BaseModel):
    email: EmailStr
    senha: str
    nome: str
    cpf: Optional[str] = None
    
    @validator('senha')
    def senha_forte(cls, v):
        erros = validar_senha(v)
        if erros:
            raise ValueError('; '.join(erros))
        return v
    
    @validator('cpf')
    def cpf_valido(cls, v):
        if v and not validar_cpf(v):
            raise ValueError('CPF inválido')
        return v
    
    @validator('nome')
    def nome_seguro(cls, v):
        # Previne injection
        if re.search(r'[<>\"\'%;()&+]', v):
            raise ValueError('Nome contém caracteres inválidos')
        return v

Resposta a Incidentes

Procedimentos por Tipo de Incidente

  1. Isolar imediatamente

    # Desativar usuário
    usuario.ativo = False
    await db.commit()
    
    # Invalidar todos os tokens
    await redis.delete(f"tokens:user:{usuario.id}:*")
  2. Auditar atividades

    SELECT * FROM audit_logs 
    WHERE user_id = ? 
    AND timestamp > NOW() - INTERVAL '7 days'
    ORDER BY timestamp DESC;
  3. Notificar usuário

  4. Forçar reset de senha

  5. Revisar permissões

  1. Verificar se já foi usada

    SELECT COUNT(*) FROM usuarios WHERE admin = true;
  2. Se não foi usada

    • Alterar imediatamente no .env
    • Reiniciar aplicação
    • Monitorar tentativas
  3. Se já foi usada

    • Master key já está bloqueada
    • Auditar criação do primeiro admin
    • Verificar integridade da conta admin
  1. Ativar modo de proteção

    # nginx.conf
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
    limit_req zone=api burst=20 nodelay;
  2. Escalar horizontalmente

    kubectl scale deployment api --replicas=10
  3. Ativar Cloudflare/WAF

  4. Bloquear IPs maliciosos

  5. Notificar equipe de segurança

  1. Identificar escopo
  2. Isolar sistemas afetados
  3. Notificar DPO (Data Protection Officer)
  4. Seguir protocolo LGPD:
    • Notificar ANPD em 72h
    • Notificar afetados
    • Documentar medidas
  5. Implementar correções
  6. Auditoria forense

Checklist de Segurança

Desenvolvimento

Nunca aceitar master_key fora do /signup Sempre validar permissões antes de operações Usar schemas específicos para cada endpoint Implementar logs de auditoria Validar e sanitizar todas as entradas Usar prepared statements (SQLAlchemy ORM) Implementar timeouts em operações externas

Deployment

HTTPS obrigatório em produção Certificados SSL/TLS válidos Headers de segurança configurados CORS restritivo Rate limiting ativo Firewall configurado Secrets em gerenciador seguro Backups criptografados

Operacional

Monitoramento 24/7 Alertas de segurança configurados Logs centralizados Rotação de secrets (90 dias) Patches de segurança mensais Pen-test trimestral Treinamento de segurança semestral Simulação de incidentes anual

Criptografia

Dados em Repouso

from cryptography.fernet import Fernet
import base64

class DataEncryption:
    def __init__(self, key: str):
        self.cipher = Fernet(base64.urlsafe_b64encode(key.encode()[:32]))
    
    def encrypt_pii(self, data: str) -> str:
        """Criptografa dados pessoais identificáveis"""
        return self.cipher.encrypt(data.encode()).decode()
    
    def decrypt_pii(self, encrypted: str) -> str:
        """Descriptografa dados pessoais"""
        return self.cipher.decrypt(encrypted.encode()).decode()

# Uso
encryption = DataEncryption(settings.ENCRYPTION_KEY)
encrypted_cpf = encryption.encrypt_pii("123.456.789-00")

Dados em Trânsito

  • TLS 1.3 mínimo
  • Cipher suites fortes
  • HSTS habilitado
  • Certificate pinning (mobile)

Conformidade e Regulamentações

LGPD (Lei Geral de Proteção de Dados)

  • Consentimento explícito para coleta
  • Direito ao esquecimento implementado
  • Portabilidade de dados
  • Logs de acesso a dados pessoais
  • DPO (Data Protection Officer) designado

Padrões de Segurança

  • OWASP Top 10 - Todas vulnerabilidades endereçadas
  • ISO 27001 - Controles implementados
  • CIS Controls - Baseline de segurança
  • NIST Cybersecurity Framework - Gestão de riscos

Contatos de Emergência

TipoContatoDisponibilidade
Security Team[email protected]24/7
DPO[email protected]Horário comercial
SOC+55 11 9999-999924/7
Incident Response[email protected]24/7

IMPORTANTE: Este documento contém informações sensíveis sobre a segurança do sistema. Mantenha-o protegido e compartilhe apenas com pessoal autorizado.

On this page