🔒 Segurança da Informação

OWASP  |  Criptografia  |  Autenticação  |  Ataques  |  Defesas

Conceitos Fundamentais e OWASP Top 10
Tríade CIA
Confidencialidade
Dados acessíveis só por quem tem permissão. Ex: criptografia, controle de acesso, JWT.
Integridade
Dados não foram alterados sem autorização. Ex: hashes, assinaturas digitais, checksums.
Disponibilidade
Sistema acessível quando necessário. Ex: redundância, backups, proteção contra DDoS.
Autenticidade
Garantir que o usuário é quem diz ser. Ex: login, MFA, certificados digitais.
OWASP Top 10 — Vulnerabilidades mais críticas em APIs
#VulnerabilidadeExemploDefesa
1Broken Object Level AuthGET /pedidos/42 — ver pedido de outro usuárioVerificar se o recurso pertence ao usuário logado
2Broken AuthenticationTokens sem expiração, senhas fracasJWT com exp, bcrypt, MFA
3Broken Object Property AuthPATCH /usuario — alterar campo "admin: true"Whitelist de campos editáveis
4Unrestricted Resource ConsumptionSem limite de requisições — DDoS fácilRate limiting por IP/usuário
5Broken Function Level AuthUsuário comum acessa rota /admin/Middleware de verificação de roles
6Unrestricted Access to Sensitive FlowsSem limite em /esqueci-senha — força brutaRate limit + captcha em fluxos sensíveis
7SSRFAPI faz fetch de URL passada pelo usuárioValidar e bloquear URLs internas
8Security MisconfigurationDebug mode em produção, CORS abertoConfiguração revisada por ambiente
9Improper Inventory ManagementAPI v1 desatualizada ainda acessívelDeprecar e remover versões antigas
10Unsafe Consumption of APIsConfiar cegamente em API de terceirosValidar e sanitizar dados externos
⚠ BOLA — o mais crítico
Broken Object Level Authorization é o ataque mais comum em APIs. Ex: usuário 1 acessa GET /pedidos/99 sendo que o pedido 99 é do usuário 2. Sempre verifique: pedido.usuario_id == usuario_logado.id
Exercício 1: O que é BOLA e como prevenir?
→ Usuário acessa recurso de outro usuário apenas mudando o ID na URL. Previne verificando propriedade do recurso.
if pedido.usuario_id != current_user.id: raise HTTPException(403). Simples assim — mas esquecido com frequência em APIs mal projetadas.
Ataques Comuns e Como Defender
SQL Injection
# VULNERÁVEL — concatenação de string na query @app.get("/usuarios") def buscar(nome: str): query = f"SELECT * FROM usuarios WHERE nome = '{nome}'" # Se nome = "'; DROP TABLE usuarios; --" # vira: SELECT * FROM usuarios WHERE nome = ''; DROP TABLE usuarios; --' return db.execute(query) # SEGURO — query parametrizada (ORM ou parâmetros) def buscar_seguro(nome: str): # SQLAlchemy — parametrizado automaticamente return db.query(Usuario).filter(Usuario.nome == nome).all() # SQL puro com parâmetros return db.execute("SELECT * FROM usuarios WHERE nome = :nome", {"nome": nome})
XSS — Cross-Site Scripting
# XSS acontece quando o backend retorna dados do usuário sem sanitizar # e o frontend renderiza como HTML # Vulnerável: retorna HTML com input do usuário comentario = request.body.comentario return f"

{comentario}

" # Se comentario = "" # o script executa no browser de quem ver! # Defesa: escape de HTML + Content-Security-Policy import html comentario_seguro = html.escape(comentario) # Header CSP no Nginx: add_header Content-Security-Policy "default-src 'self';"
CSRF — Cross-Site Request Forgery
# CSRF: site malicioso faz requisição autenticada em nome do usuário # Ex: usuário logado no banco, acessa site malicioso que faz: # POST bank.com/transferir com os cookies do usuário! # Defesa 1: CSRF Token (formulários) # Defesa 2: SameSite cookies response.set_cookie("session", value, samesite="strict") # Defesa 3: APIs REST com JWT no header (não cookie) # O header Authorization não é enviado automaticamente pelo browser # — CSRF não funciona com JWT no header!
Rate Limiting — Proteção contra Força Bruta e DDoS
pip install slowapi from slowapi import Limiter from slowapi.util import get_remote_address limiter = Limiter(key_func=get_remote_address) app.state.limiter = limiter # Limitar rotas específicas @app.post("/auth/login") @limiter.limit("5/minute") # max 5 tentativas por minuto por IP async def login(request: Request, dados: LoginSchema): ... @app.post("/auth/esqueci-senha") @limiter.limit("3/hour") # max 3 por hora async def esqueci_senha(request: Request, email: str): ...
Principais Vetores de Ataque
AtaqueComo funcionaDefesa principal
SQL InjectionInjeta SQL em inputs para manipular o bancoORM / queries parametrizadas
XSSInjeta JavaScript malicioso via inputsEscape de HTML, CSP header
CSRFSite malicioso faz requests autenticadosJWT no header, SameSite cookies
Força BrutaTesta senhas até acertarRate limit, bloqueio de IP, MFA
DDoSInunda o servidor de requisiçõesCDN, rate limit, WAF, Cloudflare
Man-in-the-MiddleIntercepta comunicaçãoHTTPS obrigatório, HSTS
Credential StuffingUsa senhas vazadas de outros sitesMFA, pwned passwords API
Exercício 2: Por que APIs REST com JWT no header são mais seguras contra CSRF?
→ O browser não envia o header Authorization automaticamente em requests cross-origin — diferente de cookies.
CSRF explora o fato de browsers enviarem cookies automaticamente para o domínio. JWT no header Authorization requer JavaScript explícito para enviar — um site malicioso não consegue fazer isso por restrições do CORS.
Criptografia
Tipos de Criptografia
Simétrica
Mesma chave para criptografar e descriptografar.

Rápida, ideal para grandes volumes.
Ex: AES-256.

Problema: como compartilhar a chave com segurança?
Assimétrica (Par de Chaves)
Chave pública (criptografa) + chave privada (descriptografa).

Mais lenta, usada para trocar chaves e assinar.
Ex: RSA, ECDSA.

Base do HTTPS e assinaturas digitais.
Hash (One-way)
Entrada → saída de tamanho fixo. Irreversível.

Ex: bcrypt (senhas), SHA-256 (integridade), MD5 (obsoleto para segurança).

Mesmo input = mesmo output sempre.
TLS/SSL
Combina assimétrica (para trocar chave de sessão) + simétrica (para dados).

HTTPS = HTTP + TLS. Garante confidencialidade e integridade da comunicação.
Hashing de Senhas — bcrypt
from passlib.context import CryptContext pwd = CryptContext(schemes=["bcrypt"], deprecated="auto") # Criar hash senha_hash = pwd.hash("minha_senha_123") # $2b$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy # Verificar pwd.verify("minha_senha_123", senha_hash) # True pwd.verify("senha_errada", senha_hash) # False # Por que bcrypt e não SHA-256 para senhas? # SHA-256: rápido demais — atacante testa bilhões por segundo # bcrypt: lento por design (fator de custo configurável) # bcrypt com cost=12 → ~100ms por hash → força bruta inviável
JWT — Assinatura e Verificação
from jose import jwt from datetime import datetime, timedelta SECRET = "chave-secreta-minima-256-bits-de-comprimento" ALGO = "HS256" # HMAC com SHA-256 # Criar token def criar_token(user_id: int, role: str) -> str: payload = { "sub": str(user_id), # subject (ID do usuário) "role": role, "iat": datetime.utcnow(), # issued at "exp": datetime.utcnow() + timedelta(minutes=30) # expira em 30min } return jwt.encode(payload, SECRET, algorithm=ALGO) # Verificar token def verificar_token(token: str) -> dict: return jwt.decode(token, SECRET, algorithms=[ALGO]) # Lança JWTError se inválido ou expirado # HS256 = assinatura simétrica (mesma chave para assinar e verificar) # RS256 = assinatura assimétrica (privada assina, pública verifica) — melhor para microsserviços
⚠ Nunca armazene dados sensíveis no JWT
O payload do JWT é apenas codificado em Base64, não criptografado. Qualquer pessoa pode decodificar e ler o conteúdo. Guarde apenas id e role — nunca senha, CPF ou dados pessoais sensíveis.
Exercício 3: Por que bcrypt é melhor que SHA-256 para senhas?
→ bcrypt é lento por design. SHA-256 é tão rápido que atacantes testam bilhões de combinações por segundo.
SHA-256 em GPU: ~10 bilhões hashes/s. bcrypt cost=12: ~100ms por hash = ~10 hashes/s. A senha "senha123" em SHA-256: quebrada em segundos. Em bcrypt: anos. O custo é configurável — aumenta com hardware mais rápido.
Segurança em APIs Python — Checklist Prático
Configurações de Segurança no FastAPI
from fastapi import FastAPI, Depends, HTTPException, Security from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.trustedhost import TrustedHostMiddleware import secrets app = FastAPI() # CORS — restrinja em produção app.add_middleware(CORSMiddleware, allow_origins=["https://meuapp.com"], # NÃO use ["*"] em produção allow_methods=["GET", "POST", "PUT", "DELETE"], allow_headers=["Authorization", "Content-Type"], allow_credentials=True, ) # Trusted Host — evita Host header attacks app.add_middleware(TrustedHostMiddleware, allowed_hosts=["meuapp.com", "api.meuapp.com"] ) # Security headers via Nginx (não no FastAPI) # add_header X-Frame-Options "DENY"; # add_header X-Content-Type-Options "nosniff"; # add_header Strict-Transport-Security "max-age=31536000"; # add_header X-XSS-Protection "1; mode=block";
Autenticação e Autorização
from enum import Enum from functools import wraps class Role(str, Enum): ADMIN = "admin" USER = "user" security = HTTPBearer() # Dependência de autenticação async def get_current_user( credentials: HTTPAuthorizationCredentials = Security(security) ): token = credentials.credentials try: payload = verificar_token(token) return payload except: raise HTTPException(401, "Token inválido ou expirado") # Dependência de autorização por role def requer_role(role: Role): async def verificar(user=Depends(get_current_user)): if user.get("role") != role: raise HTTPException(403, "Sem permissão") return user return verificar # Uso nas rotas @app.get("/admin/usuarios") async def admin_listar(user=Depends(requer_role(Role.ADMIN))): return {"msg": "só admin chega aqui"} @app.get("/perfil") async def meu_perfil(user=Depends(get_current_user)): return {"id": user["sub"], "role": user["role"]} # BOLA — verificar propriedade do recurso @app.get("/pedidos/{pedido_id}") async def get_pedido(pedido_id: int, user=Depends(get_current_user)): pedido = db.query(Pedido).get(pedido_id) if not pedido: raise HTTPException(404) if pedido.usuario_id != int(user["sub"]): # BOLA check! raise HTTPException(403, "Não é seu pedido") return pedido
Checklist de Segurança para Deploy
ItemComo verificar
HTTPS ativocurl -I https://minha-api.com → status 200
Senhas com bcryptRevisar código — nunca md5/sha1/texto puro
JWT com expiraçãoToken tem campo "exp" no payload
CORS restritoallow_origins sem "*" em produção
Rate limiting ativoTentar 100 logins/min — deve bloquear
Variáveis de ambiente.env no .gitignore, secrets no servidor
ORM em todas queriesNenhum f-string com input do usuário em SQL
Debug mode desligadoDEBUG=False em produção
Logs sem dados sensíveisRevisar logs — sem senha, CPF, token
BOLA verificadoTestar acessar recurso de outro usuário → 403
📝 Cola — Segurança da Informação
Tríade CIA
PilarGarantiaComo
ConfidencialidadeSó quem tem permissão acessaCriptografia, JWT, RBAC
IntegridadeDados não foram alteradosHash, assinatura digital
DisponibilidadeSistema acessívelRedundância, anti-DDoS
OWASP — Os 3 mais importantes
VulnerabilidadeDefesa
BOLA — acesso a recurso alheioVerificar pedido.user_id == current_user.id
SQL InjectionORM / queries parametrizadas — nunca f-string
Força BrutaRate limit em /login e /esqueci-senha
⭐ Regras de Ouro
Regra
Senhas: sempre bcrypt — nunca MD5, SHA1 ou texto puro
Regra
JWT: só id e role no payload — nunca dados sensíveis (é Base64, não criptografado)
RegraSQL: nunca concatenar input do usuário — use ORM ou parâmetros
Regra
BOLA: sempre verifique se o recurso pertence ao usuário logado
Regra
CORS: nunca "*" em produção — liste os domínios permitidos
Regra
401 = não autenticado. 403 = autenticado mas sem permissão