Intermediário Web — OWASP Top 10
Controle de Acesso Quebrado e IDOR
Controle de acesso quebrado (Broken Access Control) é a principal categoria do OWASP Top 10. Acontece quando a aplicação não verifica adequadamente se o usuário autenticado tem permissão para executar uma ação ou acessar um recurso específico.
IDOR — Insecure Direct Object Reference
O objeto identificado na URL ou no corpo da requisição é acessível sem verificar se pertence ao usuário atual.
Usuário A (id=42) acessa seu próprio pedido:
GET /api/orders/1001
Authorization: Bearer <token do usuário 42>
Troca o id:
GET /api/orders/1002
Resposta: dados do pedido de outro usuário — IDOR confirmado
Escalonamento de privilégio horizontal vs vertical
Horizontal: usuário A acessa dados do usuário B (mesmo nível de privilégio). Vertical: usuário comum acessa funcionalidade de admin.
// Vertical — parâmetro de papel no request
POST /api/users/update
{"user_id": 99, "role": "admin"}
Se o servidor aceitar sem checar se o requisitante é admin → escalonamento vertical.
Código vulnerável vs seguro
# VULNERÁVEL — usa o id da URL diretamente
@app.route('/api/orders/<int:order_id>')
def get_order(order_id):
order = Order.query.get(order_id)
return jsonify(order.to_dict())
# SEGURO — filtra pelo usuário autenticado
@app.route('/api/orders/<int:order_id>')
@login_required
def get_order(order_id):
order = Order.query.filter_by(
id=order_id,
user_id=current_user.id # garante que pertence ao usuário
).first_or_404()
return jsonify(order.to_dict())
Referências indiretas — mitigação de IDOR
Troque IDs sequenciais por UUIDs ou tokens opacos. UUIDs v4 são aleatórios e imprevisíveis, dificultando enumeração.
// Enumerável — atacante testa 1001, 1002, 1003...
GET /api/orders/1001
// Não enumerável
GET /api/orders/f47ac10b-58cc-4372-a567-0e02b2c3d479
Mas isso não substitui a verificação de propriedade no servidor — é uma defesa em profundidade.
Controle de acesso baseado em função (RBAC)
def require_role(*roles):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
if current_user.role not in roles:
abort(403)
return f(*args, **kwargs)
return wrapper
return decorator
@app.route('/admin/users')
@login_required
@require_role('admin', 'superadmin')
def list_users():
...
Boas práticas
- Negar por padrão: se não há regra explícita de permissão, rejeite.
- Verificar autorização no servidor — nunca confiar em controles do frontend.
- Logar todos os 403 e alertar em volumes altos (pode indicar varredura de IDOR).
- Testes automatizados de autorização: chamar endpoints com tokens de outros usuários e verificar que retornam 403.
- Revisão periódica de matriz de permissões vs funcionalidades.