Les nonces sont une boîte de Pandore.
Non, vraiment, l'une des motivations de plusieurs CAESAR Les entrées consistaient à concevoir un schéma de chiffrement authentifié, de préférence basé sur un chiffrement à flux, qui soit résistant à la réutilisation des nonces. (La réutilisation d'un nonce avec AES-CTR, par exemple, détruit la confidentialité de votre message au point qu'un étudiant en première année de programmation pourrait le décrypter).
Il existe trois grandes écoles de pensée concernant les nonces :
- Dans la cryptographie à clé symétrique : Utiliser un compteur croissant, en prenant soin de ne jamais le réutiliser. (Cela signifie également utiliser un compteur distinct pour l'expéditeur et le destinataire). Cela nécessite une programmation avec état (c'est-à-dire stocker le nonce quelque part pour que chaque demande ne commence pas à
1
).
- Nonces aléatoires avec état. Générer un nonce aléatoire et s'en souvenir pour le valider ultérieurement. C'est la stratégie utilisée pour vaincre les attaques CSRF, ce qui semble plus proche de ce qui est demandé ici.
- Nonces aléatoires apatrides de grande taille. Si l'on dispose d'un générateur de nombres aléatoires sûr, on peut presque garantir qu'un nonce ne sera jamais répété deux fois au cours de sa vie. C'est la stratégie utilisée par NaCl pour le cryptage.
En gardant cela à l'esprit, les principales questions à poser sont les suivantes :
- Lesquelles de ces écoles de pensée sont les plus pertinentes pour le problème que vous essayez de résoudre ?
- Comment générez-vous le nonce ?
- Comment validez-vous le nonce ?
Génération d'un Nonce
La réponse à la question 2 pour tout nonce aléatoire est d'utiliser un CSPRNG. Pour les projets PHP, cela signifie l'un des deux :
-
random_bytes()
pour les projets PHP 7+.
-
paragonie/random_compat un polyfill PHP 5 pour
random_bytes()
-
ircmaxell/RandomLib qui est un couteau suisse d'utilitaires d'aléatoire que la plupart des projets qui traitent de l'aléatoire (par exemple, la réinitialisation des mots de passe) devraient envisager d'utiliser au lieu de créer leurs propres utilitaires.
Ces deux éléments sont moralement équivalents :
$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$_SESSION['nonce'] [] = $generator->generate(32);
et
$_SESSION['nonce'] []= random_bytes(32);
Validation d'un nonce
Stateful
Les nonces avec état sont faciles et recommandés :
$found = array_search($nonce, $_SESSION['nonces']);
if (!$found) {
throw new Exception("Nonce not found! Handle this or the app crashes");
}
// Yay, now delete it.
unset($_SESSION['nonce'][$found]);
N'hésitez pas à remplacer le array_search()
avec une base de données ou une consultation de memcached, etc.
Apatrides (ici les dragons)
C'est un problème difficile à résoudre : Vous devez trouver un moyen d'empêcher les attaques par rejeu, mais votre serveur est totalement amnésique après chaque requête HTTP.
La seule solution sensée serait d'authentifier une date/heure d'expiration afin de minimiser l'utilité des attaques par rejeu. Par exemple :
// Generating a message bearing a nonce
$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
);
// Validating a message and retrieving the nonce
$decoded = base64_decode($input);
if ($decoded === false) {
throw new Exception("Encoding error");
}
$mac = mb_substr($decoded, 0, 32, '8bit'); // stored
$message = mb_substr($decoded, 32, null, '8bit');
$calc = hash_hmac('sha256', $message, $authenticationKey, true); // calcuated
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; // Valid (for one hour)
Un observateur attentif notera qu'il s'agit essentiellement d'une variante non conforme aux normes de l'option Jetons Web JSON .
1 votes
Sachez que tout ce que fait un jeu flash sur votre client peut être reproduit par quelqu'un disposant d'un décompilateur/renifleur de paquets et de suffisamment de temps. Toute protection que vous ajoutez peut donc être mise en échec.
0 votes
Ce qui m'intéresse, c'est d'augmenter la quantité de temps investie pour se maintenir faussement en vie. Oui, ils peuvent le décompiler et le remplacer, mais l'algorithme de hachage n'est pas un secret et ne protège que parce qu'il a un sel secret, que s'ils sont intelligents, ils peuvent trouver avec une table arc-en-ciel.
13 votes
C'est pourquoi le marteau d'interdiction a été inventé.
3 votes
@cdhowie - Pas vraiment, vous pouvez enregistrer une partie d'un jeu et la rejouer sur le serveur, puis prendre le score obtenu lors de la relecture, pour faire court. Une horreur de codage cependant ;).
0 votes
Maurycy : Cela n'empêcherait pas de rejouer le même jeu encore et encore. Cela n'empêcherait pas non plus les gens d'implémenter leur propre générateur d'enregistrements de jeux.
0 votes
Quelles valeurs avez-vous à votre disposition pour créer le nonce ? Existe-t-il un système de login ? Pouvez-vous expliquer un peu mieux le scoring ?
0 votes
Il existe un système de connexion. Les scores des jeux sont enregistrés pour un utilisateur. Les utilisateurs disposent également d'un autre système de notation qui correspond à leur degré d'activité sur le site. Ils obtiennent un point chaque fois qu'ils jouent à un jeu (ou plus s'ils obtiennent un score élevé). Ce qui se passe, c'est que l'utilisateur gonfle artificiellement son score d'"activité sur le site".
0 votes
@cdhowie : Vous pouvez vous protéger contre le rejeu en rejetant tout jeu de réplique parfaite, en partant du principe raisonnable qu'un vrai joueur introduirait d'infimes différences de timing d'une partie à l'autre.