Ihr Service hat einen API-Key für Ihren Cloud-Speicher. Ein Nutzer schickt eine Anfrage. Ihr Service schreibt brav eine Datei. Die Datei landet im Bucket des Angreifers, nicht in Ihrem, und jetzt hat er Ihre Daten. Sie wurden gerade zum unwissentlichen Komplizen.
Das ist eine Confused-Deputy-Attacke. Der Angreifer kann nicht selbst in den Ziel-Bucket schreiben. Ihm fehlt die Berechtigung. Aber er kann Ihren Service täuschen, der die Berechtigung hat, damit er es für ihn tut. Ihr Service ist der “Deputy”. Er ist “confused” – verwirrt – darüber, für wen die Aktion wirklich ausgeführt wird.
Wie eine Confused-Deputy-Attacke in der Praxis aussieht
Das klassische Beispiel ist ein Compiler. Der Compiler läuft mit den Rechten des Nutzers, damit er dessen Quelldateien lesen und Object-Files in dessen Home-Verzeichnis schreiben kann. Ein bösartiger Nutzer füttert den Compiler mit einer Quelldatei, die Direktiven enthält, die Ausgabe nach /etc/passwd statt in das Build-Verzeichnis zu schreiben. Der Compiler hat die Autorität. Der Nutzer nicht. Der Compiler ist verwirrt, wessen Absicht er ausführt.
Moderne Web-Services reproduzieren dieses Muster ständig. Betrachten wir einen File-Upload-Service, der Nutzern erlaubt, eine Callback-URL anzugeben:
# 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}")
Das sieht harmlos aus. Der Nutzer gibt eine URL an. Sie holen sie ab. Sie speichern sie in Ihrem Bucket. Aber was, wenn source_url http://169.254.169.254/latest/meta-data/iam/security-credentials/ ist? Jetzt exfiltrieren Sie Ihre eigenen AWS-Credentials und schreiben sie in einen Pfad, den der Angreifer benannt hat. Ihre IAM-Rolle hat die Autorität. Der Angreifer nicht. Sie sind der Confused Deputy.
Die Attacke lässt sich auf jedes System verallgemeinern, in dem eine privilegierte Komponente eine Aktion im Namen eines weniger privilegierten Anfragenden ausführt und der Anfragende die Parameter dieser Aktion beeinflussen kann.
Warum Identity Checks nicht ausreichen
Der reflexartige Fix ist, zu prüfen, wer der Anfragende ist. “Ist dieser Nutzer authentifiziert?” Ja. “Darf dieser Nutzer Dateien hochladen?” Ja. Beide Prüfungen bestehen – und die Attacke funktioniert trotzdem.
Das Problem ist, dass Authorization-Checks verifizieren, ob der Anfragende eine Aktion ausführen darf. Sie verifizieren nicht, ob das Ziel der Aktion für diesen Anfragenden angemessen ist. Der Nutzer darf Dateien hochladen. Er darf aber nicht Ihren Service dazu bringen, seine eigenen Metadaten abzurufen und unter seiner Kontrolle zu speichern. Das Permission-Model vermischt “darf den Upload-Endpunkt nutzen” mit “kann den Service dazu bringen, beliebige privilegierte Aktionen auszuführen”.
Das ist der zentrale Fehlermodus: der Deputy hält eine Capability (AWS-Credentials, Filesystem-Zugriff, Database-Write-Access) und setzt sie basierend auf Anweisungen von jemand anderem ein. Ohne die Capability an die spezifische Absicht des ursprünglichen Autoritätshalters zu binden, ist Verwirrung unvermeidlich.
Capability-Based Security: Autorität an Absicht binden
Capability-Based Security behebt das, indem sie Autorität unverfälschbar und explizit macht. Statt zu fragen “wer bist du?” und dann nachzuschlagen, was du darfst, ist eine Capability ein Token, das direkt das Recht vermittelt, eine spezifische Aktion an einer spezifischen Ressource auszuführen.
In einem Capability-System würde der Upload-Service keinen generellen S3-Write-Key halten. Er würde eine Capability halten, um in spezifische Pfade zu schreiben, die auf spezifische Nutzer begrenzt sind. Die Anfrage des Nutzers würde ein Capability-Token enthalten, das das exakte Ziel benennt. Der Service würde die kryptografische Signatur und den Scope des Tokens verifizieren, nicht die Identität des Nutzers gegen eine globale Permission-Tabelle.
Hier ist ein vereinfachtes Beispiel mit 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)
Der entscheidende Einblick: das Capability-Token scoped_token kann nicht wiederverwendet werden, um nach uploads/bob/salary.xlsx zu schreiben. Es ist zur Erstellungszeit an eine spezifische Ressource gebunden. Der Service entscheidet nicht basierend darauf, wer fragt, was erlaubt ist. Er entscheidet basierend darauf, was das Token explizit autorisiert.
Wenn Capability-Systeme unpraktikabel sind
Die meisten von uns bauen keine Betriebssysteme mit reinen Capability-Architekturen. Wir rufen AWS-APIs auf, sprechen mit PostgreSQL und schreiben HTTP-Handler. Full Capability-Based Security ist eine Design-Philosophie, keine Drop-in-Library.
Der praktische Mittelweg ist, Capability-Denken auf Ihre Service-Grenzen anzuwenden. Statt Ihrem Service eine einzelne IAM-Rolle mit breiten S3-Berechtigungen zu geben, nutzen Sie Scoped Presigned URLs. Statt Nutzern zu erlauben, beliebige Ziele zu benennen, validieren und sanitizen Sie die Zielressource gegen das, was dieser Nutzer berühren darf. Das Ziel ist kein formales Capability-OS. Das Ziel ist, implizite Autorität zu eliminieren.
Hier ist derselbe Upload-Endpunkt mit Scoped Validation statt 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)
Das ist kein Capability-System im formalen Sinn. Aber es wendet dasselbe Prinzip an: die Autorität des Service ist kein Blankoscheck. Sie ist auf einen spezifischen Satz von Aktionen begrenzt, die der Anfragende aufrufen darf.
Warum dieses Muster immer wieder auftaucht
Confused-Deputy-Schwachstellen tauchen in OAuth-Flows, Serverless-Funktionen, Webhook-Handlern und Supply-Chain-Pipelines auf. Jedes Mal, wenn eine Komponente mit erhöhten Rechten Nutzerinput verarbeitet und danach handelt, besteht das Risiko.
Das SSRF-to-Metadata-Exfiltration-Muster ist so verbreitet, dass Cloud-Provider die IMDS-IP jetzt standardmäßig in VPCs blockieren. Das ist eine Mitigation für ein Symptom, nicht für die Krankheit. Die Krankheit ist eine privilegierte Komponente, die ihre eigene Absicht nicht von der Absicht des Anfragenden unterscheiden kann.
Confused-Deputy-Attacken in der Praxis verhindern
Sie müssen Ihre Architektur nicht umschreiben. Drei Gewohnheiten decken die meisten Fälle ab.
Scopen Sie Ihre Credentials. Wenn Ihr Service mit S3 kommuniziert, geben Sie ihm eine IAM-Policy, die nur in ein spezifisches Prefix schreiben kann. Wenn er mit einer Datenbank spricht, nutzen Sie Row-Level Security oder separate Credentials pro Tenant. Je kleiner der Blast-Radius eines Confused Deputy, desto weniger Schaden kann er anrichten.
Validieren Sie das Ziel, nicht nur den Actor. Authorization-Checks sollten zwei Fragen beantworten: darf der Anfragende diese Aktion ausführen, und liegt das Ziel der Aktion im Scope des Anfragenden? Ein Nutzer darf Dateien hochladen. Darf er Dateien in das Verzeichnis eines anderen Nutzers hochladen? Kann er Ihren Service dazu bringen, beliebige URLs aufzurufen? Die zweite Frage ist, wo Confused Deputies leben.
Vermeiden Sie Ambient Authority. Ambient Authority bedeutet, dass ein Prozess Berechtigungen einfach hat, weil er ist, wer er ist, nicht weil er tut, was er tut. Als root laufen. Einen Master-API-Key nutzen. Eine Database-Superuser-Verbindung halten. Das ist bequem und gefährlich. Teilen Sie Ihre Privilegien auf. Nutzen Sie temporäre Credentials mit expliziter Ablaufzeit. Je granularer Ihre Autorität, desto schwerer lässt sie sich verwirren.
FAQ
Ist CSRF eine Confused-Deputy-Attacke?
Ja, in gewisser Weise. Der Browser des Nutzers ist der Deputy. Er hält ein Session-Cookie (die Capability) und führt eine state-changing Action aus, weil eine bösartige Seite es ihm gesagt hat. Der Browser ist verwirrt darüber, ob die Anfrage die Absicht des Nutzers repräsentiert. CSRF-Tokens funktionieren, indem sie die Capability (das Cookie) an einen spezifischen Action-Kontext binden – genau der Capability-Based Fix.
Gilt das auch für Microservices?
Absolut. Service-to-Service-Calls nutzen oft einen Shared Service Account mit breiten Berechtigungen. Wenn Service A Service B mit einem nutzerbereitgestellten Parameter aufruft, und Service B seinen privilegierten Account nutzt, um auf diesen Parameter zu handeln, ist Service B der Confused Deputy. Nutzen Sie Scoped Service Tokens oder Request-Specific Authorization.
Was ist mit SQL Injection?
SQL Injection ist eine Confused-Deputy-Attacke auf Ihre Datenbank. Die Datenbank ist der Deputy. Sie hat die Autorität, Tabellen zu lesen und zu schreiben. Der Angreifer füttert sie mit Anweisungen, die als Input getarnt sind. Parameterized Queries beheben das, indem sie die Autorität der Datenbank (die Query-Struktur) vom Nutzerinput (den Parametern) trennen.
Auditieren Sie Ihre Deputies
Gehen Sie durch Ihre Services und identifizieren Sie diejenigen, die Privilegien halten, die Nutzer nicht haben. Fragen Sie für jeden: könnte ein Nutzer diesen Service dazu bringen, seine Autorität auf eine Weise einzusetzen, die dem Angreifer nützt? Wenn die Antwort ja ist, haben Sie einen Confused Deputy. Der Fix ist selten exotisch. Es geht meist darum, zu scopen, was der Service bereit ist zu tun, und Anfragen abzulehnen, die außerhalb dieses Scope liegen.
Ihr Service sollte kein Komplize sein. Stellen Sie sicher, dass er weiß, wessen Befehle er befolgt.