Signatur
Beschreibung
Pbkdf2Params ist ein einfaches Wörterbuch (Dictionary) der Web Crypto API, also keine Klasse mit Konstruktor. Es beschreibt, wie der Algorithmus PBKDF2 (Password-Based Key Derivation Function 2, RFC 8018) aus einem Passwort einen kryptografischen Schlüssel ableiten soll. Das Objekt wird als algorithm-Argument an SubtleCrypto.deriveKey() oder SubtleCrypto.deriveBits() übergeben.
Typischerweise wird PBKDF2 eingesetzt, wenn ein Benutzerpasswort in einen symmetrischen Schlüssel umgewandelt werden soll, zum Beispiel für clientseitige Verschlüsselung mit AES-GCM. Der Ablauf ist immer gleich: Zuerst wird das Passwort mit importKey("raw", …, "PBKDF2", false, ["deriveKey"]) als Basisschlüssel importiert. Danach leitet deriveKey() mit einem Pbkdf2Params-Objekt den eigentlichen Schlüssel ab.
Wie sicher das Ergebnis ist, hängt vor allem von zwei Dingen ab: einem zufälligen, pro Passwort eindeutigen Salt und einer hohen Iterationszahl. Beide Werte sind nicht geheim. Sie müssen aber zusammen mit den verschlüsselten Daten gespeichert werden, sonst lässt sich derselbe Schlüssel später nicht erneut ableiten.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $name Pflicht | string | Muss "PBKDF2" sein. |
|
| $hash Pflicht | string | { name: string } | Hash-Funktion für die interne HMAC-Berechnung: "SHA-256", "SHA-384" oder "SHA-512". "SHA-1" ist technisch erlaubt, wird aber nicht empfohlen. |
|
| $salt Pflicht | ArrayBuffer | TypedArray | DataView | Zufälliger Salt, mindestens 16 Byte lang. Am besten mit crypto.getRandomValues(new Uint8Array(16)) erzeugen. Er muss nicht geheim bleiben, aber für jedes Passwort eindeutig sein. |
|
| $iterations Pflicht | number | Anzahl der Iterationen. OWASP empfiehlt (Stand 2023) mindestens 600000 bei SHA-256 bzw. 210000 bei SHA-512. |
Rückgabewert
algorithm-Argument an deriveKey() oder deriveBits() übergeben wird. Es hat keinen eigenen Rückgabewert.Beispiele
AES-GCM-Schlüssel aus einem Passwort ableiten
const deriveAesKey = async (password, salt) => {
const enc = new TextEncoder();
// Passwort als Basisschlüssel importieren
const baseKey = await crypto.subtle.importKey(
'raw', enc.encode(password), 'PBKDF2', false, ['deriveKey']
);
/** @type {Pbkdf2Params} */
const params = {
name: 'PBKDF2',
hash: 'SHA-256',
salt,
iterations: 600_000
};
return crypto.subtle.deriveKey(
params,
baseKey,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
};
const salt = crypto.getRandomValues(new Uint8Array(16));
const key = await deriveAesKey('geheim123', salt);
console.log(key.algorithm);
Text verschlüsseln, Salt und IV mitspeichern
const encryptText = async (password, text) => {
const enc = new TextEncoder();
const salt = crypto.getRandomValues(new Uint8Array(16));
const iv = crypto.getRandomValues(new Uint8Array(12));
const baseKey = await crypto.subtle.importKey('raw', enc.encode(password), 'PBKDF2', false, ['deriveKey']);
const key = await crypto.subtle.deriveKey(
{ name: 'PBKDF2', hash: 'SHA-256', salt, iterations: 600_000 },
baseKey,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt']
);
const ciphertext = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, enc.encode(text));
// Salt, IV und Iterationszahl werden zum Entschlüsseln gebraucht
return { salt, iv, iterations: 600_000, ciphertext: new Uint8Array(ciphertext) };
};
const result = await encryptText('geheim123', 'Hallo Welt');
console.log(result.salt.length, result.iv.length);
Rohe Bits mit deriveBits() erzeugen
const enc = new TextEncoder();
const baseKey = await crypto.subtle.importKey('raw', enc.encode('passwort'), 'PBKDF2', false, ['deriveBits']);
const bits = await crypto.subtle.deriveBits(
{ name: 'PBKDF2', hash: 'SHA-512', salt: enc.encode('fester-salt-nur-demo'), iterations: 210_000 },
baseKey,
256
);
const hex = [...new Uint8Array(bits)].map(b => b.toString(16).padStart(2, '0')).join('');
console.log(hex.length);
// Wichtig · Fallstricke
- Kein fester Salt: Ein fester oder vorhersehbarer Salt macht Angriffe mit vorberechneten Tabellen (Rainbow Tables) möglich. Erzeuge den Salt deshalb immer mit
crypto.getRandomValues(). Der feste Salt in Beispiel 3 dient nur der Demonstration. - Iterationszahl: Niedrige Werte wie
1000aus älteren Anleitungen sind heute unsicher. Speichere die Iterationszahl zusammen mit den Daten. Dann kannst du sie später erhöhen, ohne bestehende Daten unlesbar zu machen. - Basisschlüssel: Der Basisschlüssel muss mit dem Algorithmus
"PBKDF2"undextractable: falseimportiert werden. Außerdem braucht er die passende Verwendung (deriveKeyoderderiveBits). - Serverseitige Passwortspeicherung: PBKDF2 ist GPU-freundlich. Für das Speichern von Passwörtern auf dem Server sind speicherintensive Verfahren wie Argon2 oder scrypt besser geeignet. Diese bietet die Web Crypto API allerdings nicht an.
- Kompatibilität:
crypto.subtleist nur in sicheren Kontexten (HTTPS oder localhost) verfügbar. Unterstützt wird es von allen modernen Browsern sowie von Node.js ab Version 15 (überrequire('crypto').webcrypto, global ab Version 19). Beiiterations: 0wird die Operation mit einemOperationErrorabgelehnt.