FlawOpen

flawopen.com/SQL Injection/Numerische IDs

Warum ist String-Formatierung selbst bei numerischen IDs gefährlich?

Referenzseite
Kurzantwort

Das Risiko hängt nicht von der Bedeutung des Werts ab: Die Abfrage entsteht durch das Aneinanderfügen von Text. „Das ist immer eine Zahl“ ist eine Annahme über die Daten, keine Garantie des Codes.

Wo die Annahme scheitert

HTTP-Parameter kommen immer als Strings an

Ein URL-Parameter oder Formularfeld wird als Text übertragen, unabhängig vom erwarteten Typ der Datenbankspalte. Ohne ausdrückliche Validierung hindert das Framework niemanden daran, id=1 OR 1=1 statt einer Zahl zu senden, bevor Ihr Code den Wert erhält.

Ein späterer Refaktor kann die Quelle des Werts unbemerkt ändern

Code, der heute sicher ist, weil die ID stets aus einer vertrauenswürdigen internen Quelle stammt, kann unsicher werden, wenn ein neuer Aufrufer einen Benutzerwert an dieselbe Funktion übergibt. Die Schwachstelle war bereits latent vorhanden.

Typumwandlung kann den tatsächlichen Wert verschleiern

Gerade in dynamisch typisierten Sprachen kann ein Wert in Logs numerisch aussehen und trotzdem ein String mit zusätzlichen Zeichen sein. Ohne explizite Validierung stimmen erwarteter und tatsächlicher Typ nicht zwingend überein.

Abfrage auch nach Typprüfung parametrisieren

Validieren Sie die ID anhand des von der Anwendung erwarteten Formats und übergeben Sie sie anschließend über die Parameter-API des Datenbanktreibers. Die Validierung schützt den Eingabevertrag; die Parametrisierung verhindert, dass die Eingabe als SQL-Syntax interpretiert wird. Das Protokollverhalten des Treibers kann variieren. Nehmen Sie daher nicht an, dass alle Treiber Werte gleich übertragen. PostgreSQL-Parameter können keine Tabellen- oder Spaltenbezeichner darstellen; wählen Sie dynamische Abfragestrukturen aus einer festen anwendungsseitigen Allowlist.

Numerischen Wert binden und Bedeutung validieren

Validieren Sie eine ID als positive sichere Ganzzahl, wenn die Anwendung diesen Typ verlangt, und binden Sie sie trotzdem als Abfragewert. Die Validierung schützt den Eingabevertrag der Anwendung; die Parametrisierung hält Eingaben aus der SQL-Syntax heraus. PostgreSQL-Parameter können keine Tabellen- oder Spaltenbezeichner ersetzen. Wählen Sie dynamische Abfragestrukturen daher aus einer festen, anwendungsseitigen Allowlist.

UNSICHER — ID in SQL interpolieren
const rawId = new URL(request.url).searchParams.get("id");
const result = await pool.query(
  `SELECT id FROM users WHERE id = ${rawId}`
);
SICHER — ID validieren und als Wert binden
const rawId = new URL(request.url).searchParams.get("id");
if (rawId === null || !/^[0-9]+$/.test(rawId)) {
  return new Response("Invalid id", { status: 400 });
}
const id = Number(rawId);
if (!Number.isSafeInteger(id) || id < 1) {
  return new Response("Invalid id", { status: 400 });
}
const result = await pool.query(
  "SELECT id FROM users WHERE id = $1",
  Array.of(id)
);

Quellen

Siehe auch SQL-Injection (CWE-89): Funktionsweise und Schutz in jeder Sprache — flawopen.com Was ist eine parametrisierte Abfrage? Ist Prisma $queryRaw vor SQL-Injection sicher? Ist String-Interpolation in Rails where() sicher? Ist Sequelize query() vor SQL-Injection sicher? Ist SQLAlchemy text() vor SQL-Injection sicher? Verhindert sqlx-Abfrageprüfung zur Kompilierzeit SQL-Injection?