O seu serviço tem uma chave de API para o seu armazenamento em nuvem. Um utilizador envia um pedido. O seu serviço obedientemente escreve um ficheiro. O ficheiro vai parar ao bucket do atacante, não ao seu, e agora eles têm os seus dados. Acabou de se tornar um cúmplice inadvertido.

Isto é um ataque confused deputy. O atacante não consegue escrever no bucket de destino por si próprio. Não tem a permissão. Mas consegue enganar o seu serviço, que tem a permissão, para que o faça por eles. O seu serviço é o “deputy”. Está “confused” sobre para quem a action é realmente destinada.

Como um Ataque Confused Deputy Se Parece na Realidade

O exemplo clássico é um compiler. O compiler executa com os privilégios do seu utilizador, por isso pode ler os seus ficheiros de origem e escrever ficheiros objeto no seu diretório pessoal. Um utilizador malicioso fornece ao compiler um ficheiro de origem que contém diretivas para escrever o resultado em /etc/passwd em vez do diretório de compilação. O compiler tem a autoridade. O utilizador não tem. O compiler está confused sobre de quem é a intenção que está a executar.

Os serviços web modernos recriam este padrão constantemente. Considere um serviço de upload de ficheiros que permite aos utilizadores fornecerem um callback URL:

# The user provides a URL. We fetch it and store it.
def upload_from_url(user_id: str, source_url: str, dest_path: str):
    response = requests.get(source_url, timeout=30)
    s3.put_object(Bucket="my-app-bucket", Key=dest_path, Body=response.content)
    audit_log.info(f"User {user_id} uploaded to {dest_path}")

Isto parece inocente. O utilizador fornece um URL. Você obtém-no. Armazena-o no seu bucket. Mas e se o source_url for http://169.254.169.254/latest/meta-data/iam/security-credentials/? Agora está a exfiltrar as suas próprias credenciais AWS e a escrevê-las num caminho que o atacante nomeou. O seu papel IAM tem a autoridade. O atacante não tem. Você é o confused deputy.

O ataque generaliza-se para qualquer sistema onde um componente privilegiado executa uma action em nome de um solicitante menos privilegiado, e o solicitante pode influenciar os parâmetros dessa action.

Por Que As Verificações de Identidade Não São Suficientes

A correção instintiva é verificar quem é o solicitante. “Este utilizador está autenticado?” Sim. “Este utilizador está autorizado a fazer upload de ficheiros?” Sim. Ambas as verificações passam, e o ataque ainda funciona.

O problema é que as verificações de autorização verificam se o solicitante pode executar uma action. Não verificam se o alvo da action é apropriado para esse solicitante. O utilizador tem permissão para fazer upload. Não tem permissão para fazer o seu serviço obter os seus próprios metadados e armazená-los sob o seu controlo. O modelo de permissões confunde “pode usar o endpoint de upload” com “pode fazer o serviço executar actions privilegiadas arbitrárias”.

Este é o modo de falha central: o deputy detém uma capability (credenciais AWS, acesso ao sistema de ficheiros, acesso de escrita à base de dados) e usa-a com base em instruções de outra pessoa. Sem vincular a capability à intenção específica do detentor original da autoridade, a confusion é inevitável.

Capability-Based Security: Vincular Autoridade à Intenção

A capability-based security corrige isto tornando a autoridade unforgeable e explícita. Em vez de perguntar “quem é você?” e depois procurar o que tem permissão para fazer, uma capability é um token que transmite diretamente o right de executar uma action específica num recurso específico.

Num capability system, o serviço de upload não deteria uma blanket S3 write key. Deteria uma capability para escrever em specific paths, scoped a specific users. O pedido do utilizador incluiria um capability token que nomeia o destino exato. O serviço verificaria a cryptographic signature do token e o seu scope, não a identidade do utilizador contra uma global permission table.

Aqui está um exemplo simplificado usando scoped tokens:

import hashlib
import hmac
import secrets

SECRET = secrets.token_bytes(32)

def mint_capability(principal: str, action: str, resource: str) -> str:
    """Create an unforgeable capability token scoped to a specific action and resource."""
    payload = f"{principal}:{action}:{resource}"
    sig = hmac.new(SECRET, payload.encode(), hashlib.sha256).hexdigest()[:16]
    return f"{payload}:{sig}"

def verify_capability(token: str, expected_action: str, expected_resource: str) -> bool:
    """Verify a capability token matches the expected action and resource."""
    try:
        principal, action, resource, sig = token.rsplit(":", 3)
    except ValueError:
        return False
    payload = f"{principal}:{action}:{resource}"
    expected_sig = hmac.new(SECRET, payload.encode(), hashlib.sha256).hexdigest()[:16]
    return hmac.compare_digest(sig, expected_sig) and action == expected_action and resource == expected_resource

# The user receives a capability that only allows writing to their own prefix.
user_id = "alice"
scoped_token = mint_capability(user_id, "write", f"uploads/{user_id}/report.pdf")

# Later, the service verifies the capability before acting.
if not verify_capability(scoped_token, "write", "uploads/alice/report.pdf"):
    raise PermissionError("Capability does not match requested action or resource")

s3.put_object(Bucket="my-app-bucket", Key="uploads/alice/report.pdf", Body=data)

A ideia-chave: o capability token scoped_token não pode ser replayed para escrever em uploads/bob/salary.xlsx. Está bound a um recurso específico no momento da criação. O serviço não decide o que é permitido com base em quem está a pedir. Decide com base no que o token autoriza explicitamente.

Quando Os Capability Systems São Impraticáveis

A maioria de nodes não está a construir operating systems com pure capability architectures. Estamos a chamar APIs AWS, a comunicar com PostgreSQL, e a escrever HTTP handlers. Full capability-based security é uma design philosophy, não uma drop-in library.

O meio-termo prático é aplicar o capability thinking aos limites do seu serviço. Em vez de dar ao seu serviço um único IAM role com broad S3 permissions, use scoped presigned URLs. Em vez de deixar os utilizadores nomearem destinos arbitrários, valide e sanitize o target resource contra o que esse utilizador tem permissão para tocar. O objetivo não é um formal capability OS. O objetivo é eliminar a implicit authority.

Aqui está o mesmo upload endpoint com scoped validation em vez de blanket trust:

import re

# A regex that constrains what a user can name as a destination.
ALLOWED_DEST_PATTERN = re.compile(r"^uploads/(?P<user_id>[a-z0-9_-]+)/[a-zA-Z0-9_.-]+$")

def upload_from_url(user_id: str, source_url: str, dest_path: str):
    match = ALLOWED_DEST_PATTERN.match(dest_path)
    if not match or match.group("user_id") != user_id:
        raise ValueError("Invalid destination path")

    # The source URL is also restricted. No internal IPs, no metadata endpoints.
    parsed = urlparse(source_url)
    if parsed.hostname in ("169.254.169.254", "localhost", "127.0.0.1"):
        raise ValueError("Forbidden source URL")

    response = requests.get(source_url, timeout=30)
    s3.put_object(Bucket="my-app-bucket", Key=dest_path, Body=response.content)

Isto não é um capability system no sentido formal. Mas aplica o mesmo princípio: a autoridade do serviço não é um blank check. Está scoped a um conjunto específico de actions que o solicitante tem permissão para invocar.

Por Que Este Padrão Continua a Reaparecer

Vulnerabilidades confused deputy aparecem em OAuth flows, serverless functions, webhook handlers, e supply-chain pipelines. Sempre que um componente com elevated privileges processa user input e age sobre ele, o risco está presente.

O padrão SSRF-to-metadata-exfiltration é tão comum que os cloud providers agora bloqueiam o IMDS IP por padrão nas VPCs. Isso é uma mitigação para um sintoma, não para a doença. A doença é um componente privilegiado que não consegue distinguir a sua própria intenção da intenção do solicitante.

Prevenir Ataques Confused Deputy na Prática

Não precisa de reescrever a sua arquitetura. Três hábitos cobrem a maioria dos casos.

Scope as suas credenciais. Se o seu serviço fala com S3, dê-lhe uma IAM policy que só pode escrever num prefixo específico. Se fala com uma base de dados, use row-level security ou credenciais separadas por tenant. Quanto menor for o blast radius de um confused deputy, menos danos pode causar.

Valide o target, não apenas o actor. As verificações de autorização devem responder a duas perguntas: o solicitante tem permissão para executar esta action, e o target da action está dentro do scope do solicitante? Um utilizador pode fazer upload de ficheiros. Pode fazer upload de ficheiros para o diretório de outro utilizador? Pode fazer o seu serviço chamar URLs arbitrários? A segunda pergunta é onde os confused deputies vivem.

Evite ambient authority. Ambient authority é quando um processo tem permissões simplesmente por causa do que é, não por causa do que está a fazer. Executar como root. Usar uma master API key. Manter uma database superuser connection. Estas são conveniências perigosas. Divida os seus privilégios. Use temporary credentials com explicit expiration. Quanto mais granular for a sua autoridade, mais difícil é confundir.

FAQ

CSRF é um ataque confused deputy?

Sim, num certo sentido. O browser do utilizador é o deputy. Detém um session cookie (a capability) e executa uma state-changing action porque um site malicioso lhe disse para o fazer. O browser está confused sobre se o pedido representa a intenção do utilizador. CSRF tokens funcionam ao binding a capability (o cookie) a um specific action context, que é exatamente a capability-based fix.

Isto aplica-se a microservices?

Absolutamente. Service-to-service calls frequentemente usam uma shared service account com broad permissions. Se o Service A chamar o Service B com um user-provided parameter, e o Service B usar a sua privileged account para agir sobre esse parâmetro, o Service B é o confused deputy. Use scoped service tokens ou request-specific authorization.

E quanto a SQL injection?

SQL injection é um ataque confused deputy na sua base de dados. A base de dados é o deputy. Tem a autoridade para ler e escrever tables. O atacante fornece-lhe instruções disfarçadas de input. Parameterized queries corrigem isto ao separar a autoridade da base de dados (a query structure) do input do utilizador (os parâmetros).

Audite Os Seus Deputies

Percorra os seus serviços e identifique os que detêm privilégios que os utilizadores não têm. Para cada um, pergunte: um utilizador poderia enganar este serviço a usar a sua autoridade de uma forma que beneficie o atacante? Se a resposta for sim, tem um confused deputy. A correção raramente é exótica. Geralmente é uma questão de scoping o que o serviço está disposto a fazer, e recusar pedidos que caiam fora desse scope.

O seu serviço não deve ser um cúmplice. Certifique-se de que sabe de quem está a seguir as ordens.