Comment créer et utiliser nonce
Je suis en cours d'exécution d'un site web, et il y a un système de notation qui vous donne des points pour le nombre de fois que vous jouez à un jeu.
Il utilise le hachage pour prouver l'intégrité de la requête http pour la notation afin que les utilisateurs ne puissent rien changer, mais comme je le craignais, quelqu'un a compris qu'ils n'avaient pas besoin de le changer, ils avaient juste besoin d'obtenir un score élevé, et dupliquer la requête http, les en-têtes et tout.
Auparavant, on m'avait interdit de me protéger contre cette attaque parce que c'était considéré comme improbable. Cependant, maintenant que c'est arrivé, je peux. La requête http provient d'un jeu flash, puis est validée par php et php l'entre dans la base de données.
Je suis assez sûr que nonces résoudra le problème, mais je ne sais pas exactement comment les implémenter. Qu'est-ce qu'un moyen commun et sécurisé de mettre en place un système nonce?
4 réponses
C'est en fait assez facile à faire... Il y a quelques bibliothèques pour le faire pour vous:
Ou si vous voulez écrire le vôtre, c'est assez simple. Utiliser la page WikiPedia comme point de départ, en pseudo-code:
Côté serveur, vous avez besoin de deux fonctions clients appelables
getNonce() {
$id = Identify Request //(either by username, session, or something)
$nonce = hash('sha512', makeRandomString());
storeNonce($id, $nonce);
return $nonce to client;
}
verifyNonce($data, $cnonce, $hash) {
$id = Identify Request
$nonce = getNonce($id); // Fetch the nonce from the last request
removeNonce($id, $nonce); //Remove the nonce from being used again!
$testHash = hash('sha512',$nonce . $cnonce . $data);
return $testHash == $hash;
}
Et du côté client:
sendData($data) {
$nonce = getNonceFromServer();
$cnonce = hash('sha512', makeRandomString());
$hash = hash('sha512', $nonce . $cnonce . $data);
$args = array('data' => $data, 'cnonce' => $cnonce, 'hash' => $hash);
sendDataToClient($args);
}
La fonction makeRandomString
vraiment juste doit renvoyer un nombre aléatoire ou une chaîne. Plus le hasard est bon, meilleure est la sécurité... Notez également que comme il est directement introduit dans une fonction de hachage, les détails d'implémentation n'ont pas d'importance d'une requête à l'autre. La version du client et la version du serveur n'ont pas besoin de correspondre. En fait, le seul bit qui doit correspondre à 100% est la fonction de hachage utilisée dans hash('sha512', $nonce . $cnonce . $data);
... Voici un exemple d'une fonction makeRandomString
raisonnablement sécurisée...
function makeRandomString($bits = 256) {
$bytes = ceil($bits / 8);
$return = '';
for ($i = 0; $i < $bytes; $i++) {
$return .= chr(mt_rand(0, 255));
}
return $return;
}
Les nonce sont une boîte de vers.
Non, vraiment, l'une des motivations de plusieurs entrées CAESAR était de concevoir un système de chiffrement authentifié, de préférence basé sur un chiffrement de flux, résistant à la réutilisation de nonce. (Réutiliser un nonce avec AES-CTR, par exemple, détruit la confidentialité de votre message dans la mesure où un étudiant en programmation de première année pourrait le déchiffrer.)
Il y a trois grandes écoles de pensée avec des nonces:
- dans la clé symétrique cryptographie: utilisez un compteur croissant, tout en prenant soin de ne jamais le réutiliser. (Cela signifie également utiliser un compteur séparé pour l'expéditeur et le récepteur.) Cela nécessite une programmation avec État (c'est-à-dire stocker le nonce quelque part afin que chaque requête ne commence pas à
1
). - nonces aléatoires avec État. Générer un nonce aléatoire, puis s'en souvenir pour valider plus tard. c'est la stratégie utilisée pour vaincre les attaques CSRF, qui semble plus proche de ce qui est demandé ici.
- Grand nonces aléatoires apatrides. Étant donné un générateur de nombres aléatoires sécurisé, vous pouvez presque garantir de ne jamais répéter un nonce deux fois dans votre vie. C'est la stratégie utilisée par NaCl pour le chiffrement.
Donc, dans cet esprit, les principales questions à poser sont:
- quelles sont les écoles de pensée ci-dessus 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érer 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 éléments suivants:
-
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 aléatoires que la plupart des projets qui traitent du hasard (par exemple, les réinitialisations de mot de passe fir) devraient envisager d'utiliser à la place de rouler leur propre
Ces deux sont moralement équivalents:
$factory = new RandomLib\Factory;
$generator = $factory->getMediumStrengthGenerator();
$_SESSION['nonce'] [] = $generator->generate(32);
Et
$_SESSION['nonce'] []= random_bytes(32);
Valider un Nonce
État
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()
par une base de données ou une recherche memcached, etc.
Sans État (ici des dragons)
C'est un problème difficile à résoudre: vous avez besoin d'un moyen d'empêcher les attaques de relecture, mais votre serveur a une amnésie totale après chaque HTTP demande.
La seule solution saine serait d'authentifier une date/heure d'expiration pour minimiser l'utilité des attaques de relecture. 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 remarquera qu'il s'agit essentiellement d'une variante non conforme aux normes des jetons web JSON.
Une option (que j'ai mentionnée dans le commentaire) est d'enregistrer le gameplay et de le rejouer dans un environnement sécurisé.
L'autre chose est d'enregistrer au hasard, ou à certains moments spécifiés, des données apparemment innocentes, qui plus tard peuvent être utilisées pour les valider sur le serveur (comme soudainement live passe de 1% à 100%, ou score de 1 à 1000 qui indiquent la triche). Avec suffisamment de données, il pourrait ne pas être possible pour le tricheur d'essayer de le simuler. Et puis bien sûr mettre en œuvre l'interdiction lourde :).
Il n'est pas possible d'empêcher la tricherie. Vous ne pouvez que le rendre plus difficile.
Si quelqu'un est venu ici à la recherche D'une bibliothèque PHP Nonce: je recommande de ne pas utiliser la première donnée par ircmaxwell .
Le premier commentaire sur le site décrit un défaut de conception:
Le nonce est bon pour une certaine fenêtre de temps, c'est-à-dire plus le l'utilisateur arrive à la fin de cette fenêtre moins il a de temps soumettre le formulaire, peut-être moins d'un deuxième
Si vous cherchez un moyen de générer des Nonces avec une durée de vie bien définie, jetez un oeil à NonceUtil-PHP .