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.