Nonces sind eine Dose Würmer.
Nein, eine der Beweggründe für mehrere CAESAR- Einträge war das Entwerfen eines authentifizierten Verschlüsselungsschemas, das vorzugsweise auf einer Stream-Verschlüsselung basiert und gegen die Nicht-Wiederverwendung resistent ist. (Die Wiederverwendung einer Nonce mit AES-CTR zerstört beispielsweise die Vertraulichkeit Ihrer Nachricht in dem Maße, wie ein Programmierstudent im ersten Jahr sie entschlüsseln könnte.)
Es gibt drei Hauptgedankensschulen mit Nonces:
- Bei der Kryptografie mit symmetrischen Schlüsseln: Verwenden Sie einen zunehmenden Zähler, und achten Sie darauf, ihn niemals wiederzuverwenden. (Dies bedeutet auch, dass ein separater Zähler für Sender und Empfänger verwendet wird.) Dies erfordert eine zustandsbehaftete Programmierung (dh das Speichern der Nonce irgendwo, damit nicht jede Anforderung bei beginnt
1
).
- Stateful zufällige Nonces. Generieren Sie eine zufällige Nonce und merken Sie sich diese, um sie später zu validieren. Dies ist die Strategie zur Abwehr von CSRF-Angriffen, die näher an dem liegt, was hier gefordert wird.
- Große staatenlose zufällige Nonces. Mit einem sicheren Zufallszahlengenerator können Sie fast garantieren, dass Sie eine Nonce in Ihrem Leben niemals zweimal wiederholen. Dies ist die Strategie, die NaCl für die Verschlüsselung verwendet.
Vor diesem Hintergrund sind folgende Fragen zu stellen:
- Welche der oben genannten Denkrichtungen sind für das Problem, das Sie lösen möchten, am relevantesten?
- Wie generierst du das Nonce?
- Wie validieren Sie die Nonce?
Nonce generieren
Die Antwort auf Frage 2 für zufällige Nonce ist die Verwendung eines CSPRNG. Für PHP-Projekte bedeutet dies Folgendes:
Diese beiden sind moralisch gleichwertig:
$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$_SESSION['nonce'] [] = $generator->generate(32);
und
$_SESSION['nonce'] []= random_bytes(32);
Nonce validieren
Staatsbürgerlich
Stateful Nonces sind einfach und zu empfehlen:
$found = array_search($nonce, $_SESSION['nonces']);
if (!$found) {
throw new Exception("Nonce not found! Handle this or the app crashes");
}
unset($_SESSION['nonce'][$found]);
Fühlen Sie sich frei, die array_search()
durch eine Datenbank oder eine zwischengespeicherte Suche usw. zu ersetzen .
Staatenlos (hier sind Drachen)
Dies ist ein schwer zu lösendes Problem: Sie benötigen eine Möglichkeit, um Wiederholungsangriffe zu verhindern, aber Ihr Server weist nach jeder HTTP-Anforderung eine vollständige Amnesie auf.
Die einzig vernünftige Lösung wäre die Authentifizierung eines Ablaufdatums / einer Ablaufzeit, um den Nutzen von Wiederholungsangriffen zu minimieren. Zum Beispiel:
$nonce = random_bytes(32);
$expires = new DateTime('now')
->add(new DateInterval('PT01H'));
$message = json_encode([
'nonce' => base64_encode($nonce),
'expires' => $expires->format('Y-m-d\TH:i:s')
]);
$publishThis = base64_encode(
hash_hmac('sha256', $message, $authenticationKey, true) . $message
);
$decoded = base64_decode($input);
if ($decoded === false) {
throw new Exception("Encoding error");
}
$mac = mb_substr($decoded, 0, 32, '8bit');
$message = mb_substr($decoded, 32, null, '8bit');
$calc = hash_hmac('sha256', $message, $authenticationKey, true);
if (!hash_equals($calc, $mac)) {
throw new Exception("Invalid MAC");
}
$message = json_decode($message);
$currTime = new DateTime('NOW');
$expireTime = new DateTime($message->expires);
if ($currTime > $expireTime) {
throw new Exception("Expired token");
}
$nonce = $message->nonce;
Ein aufmerksamer Beobachter wird feststellen, dass dies im Grunde eine nicht standardkonforme Variante von JSON-Web-Tokens ist .