FlawOpen
● Serverless-Anfrageisolierung

Serverless State Bleed: Benutzerdaten an Anfragen binden

Wie wiederverwendete Serverless-Runtimes alte Benutzerdaten offenlegen können und wie Identität und Zustand je Anfrage isoliert werden.

💡 Einfach erklärt (ELI5)

Behandeln Sie Authentifizierung, Tenant-Identität und anfragespezifische Daten wie einen Besucherausweis: Er gehört zu einem Besuch und darf nicht auf einer gemeinsam genutzten Werkbank liegen bleiben. Manche Serverless-Runtimes verwenden eine Ausführungsumgebung oder ein Isolate für spätere Anfragen erneut. Speichert der Code den Benutzerkontext als veränderlichen Modulzustand und liest eine andere Anfrage ihn aus, kann die Anwendung ihre eigene Grenze zwischen Benutzern oder Tenants überschreiten.

Wiederverwendung ist ein Implementierungsverhalten, keine Garantie, dass der nächste Benutzer dieselbe Runtime erreicht oder sie eine feste Zeit lang aktiv bleibt. Ein Leck setzt gemeinsam genutzten Ausführungszustand und einen Codepfad voraus, der Anfragedaten weiterträgt oder veralteten Zustand liest. Halten Sie Identität anfragebezogen und übergeben Sie sie explizit an benötigte Vorgänge.

Kernbegriffe

Execution environment reuse
Wiederverwendung der Ausführungsumgebung: Die Plattform kann eine Runtime oder ein Isolate für spätere Aufrufe bereithalten, sie aber jederzeit beenden.
Module-level state
Modulzustand: außerhalb des Handlers initialisierter Wert. Er kann in dieser Runtime zwischen Aufrufen bestehen bleiben und darf keine benutzer- oder tenantbezogenen Daten enthalten.
AsyncLocalStorage
AsyncLocalStorage: Node.js-API zur Weitergabe eines Stores innerhalb eines asynchronen Kontexts. Sie verfolgt Kontext, authentifiziert oder autorisiert aber niemanden.
Cross-request data exposure
Datenoffenlegung zwischen Anfragen: Eine spätere Anfrage liest Daten aus einer früheren im selben wiederverwendeten Kontext. Wiederverwendung allein beweist kein Leck.

Ablauf einer Datenoffenlegung

Step 1

Eine Anfrage erzeugt eigenen Zustand

Anfrage A authentifiziert sich und ermittelt ihren Benutzer- oder Tenantkontext. Der Fehler entsteht, wenn der Code die Identität in eine veränderliche Modulvariable schreibt statt sie im Handler zu halten.

Step 2

Die Runtime kann wiederverwendet werden

Nach der Antwort kann die Plattform die Umgebung warm halten. AWS dokumentiert die Wiederverwendung; Cloudflare Workers kann ein Isolate wiederverwenden, aber Routing und Lebensdauer bilden keine feste Abfolge zwischen Benutzern.

Step 3

Eine spätere Anfrage erreicht dieselbe Runtime

Wird Anfrage B im selben Prozess oder Isolate verarbeitet, bleibt der Modulwert von A verfügbar, bis er überschrieben oder gelöscht wird. Ein vorzeitiger Rücksprung oder fehlende Authentifizierung kann den alten Zustand erreichbar machen.

Step 4

Die Anwendung liefert Daten unter falscher Identität

Nutzt der Handler den alten Wert für eine Datenbankabfrage oder Antwort, kann B Daten von A erhalten. Das ist ein bedingter Anwendungsfehler bei der Isolation, keine unvermeidliche Eigenschaft von Serverless-Plattformen.

Quellcode: unsicheres Muster und sichere Umsetzung

UNSICHER: GETEILTER ANFRAGEZUSTAND
// UNSAFE: request identity is mutable process-wide state.
import { NextResponse } from "next/server";

let cachedUserContext: { userId: string; role: string } | null = null;

export async function POST(req: Request) {
  const token = req.headers.get("authorization");

  if (token) {
    cachedUserContext = await decodeUser(token);
  }

  // If token is missing, stale state can still be used.
  return NextResponse.json({ actor: cachedUserContext?.userId });
}
SICHERER: ANFRAGELOKALE IDENTITÄT
// SAFE PATTERN: keep identity local and pass it to the service.
import { NextResponse } from "next/server";

export async function POST(req: Request) {
  const user = await authenticate(req);
  if (!user) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  const account = await loadAccountForUser(user.id);
  return NextResponse.json({ actor: user.id, account });
}

// The service enforces ownership in its query:
async function loadAccountForUser(userId: string) {
  return db.account.findFirst({ where: { ownerId: userId } });
}

Checkliste zur technischen Härtung

← Vollständiges Sicherheitsverzeichnis durchsuchen Referenzleitfäden ansehen →

Referenzen

Siehe auch PHP == und Magic-Hash-Vergleiche Ist Rusts unsafe-Block weiterhin speichersicher? Ist strncpy() sicherer als strcpy()? CWE vs. CVE: Was ist der Unterschied? Mass Assignment: Risiken und sichere Feldfreigaben