81 votes

Comment créer et utiliser les nonces

Je gère un site web et il existe 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 l'évaluation, de sorte que les utilisateurs ne peuvent rien modifier. Cependant, comme je le craignais, quelqu'un a compris qu'il n'avait pas besoin de le modifier, qu'il lui suffisait d'obtenir un score élevé et de dupliquer la requête http, les en-têtes et tout le reste.

Auparavant, il m'avait été interdit de me protéger contre cette attaque, car elle était considérée comme improbable. Cependant, maintenant que cela s'est produit, je peux le faire. La requête http provient d'un jeu flash, puis elle est validée par php et php la saisit dans la base de données.

Je suis presque sûr que les nonces résoudront le problème, mais je ne sais pas exactement comment les mettre en œuvre. Quel est le moyen le plus courant et le plus sûr de mettre en place un système de nonces ?

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é.

63voto

ircmaxell Points 74865

C'est en fait assez facile à faire... Il existe des bibliothèques qui le font pour vous :

  1. Bibliothèque PHP Nonce
  2. Bibliothèque OpenID Nonce

Ou si vous voulez écrire le vôtre, c'est assez simple. En utilisant le Page WikiPedia comme un point de départ, en pseudo-code :

Du côté serveur, vous avez besoin de deux fonctions appelables par le client

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é du 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 a juste besoin de retourner un nombre ou une chaîne aléatoire. Plus le caractère aléatoire est grand, plus la sécurité est grande... Notez également qu'étant donné qu'il est introduit directement 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 celle du serveur n'ont pas besoin de correspondre. En fait, le seul élément qui doit correspondre à 100 % est la fonction de hachage utilisée dans l'opération suivante hash('sha512', $nonce . $cnonce . $data); ... Voici un exemple d'un système raisonnablement sûr. makeRandomString fonction...

function makeRandomString($bits = 256) {
    $bytes = ceil($bits / 8);
    $return = '';
    for ($i = 0; $i < $bytes; $i++) {
        $return .= chr(mt_rand(0, 255));
    }
    return $return;
}

2 votes

Bonne réponse, mais la génération d'un nonce puis sa transmission via http (c'est-à-dire par fil ouvert) n'empêche pas les attaques par rejeu. Le vieux problème de "pouvez-vous faire confiance au client" et la réponse est invariablement "non". A moins que vous n'ayez une logique de session côté serveur, c'est difficile.

5 votes

@zebrabox, les Nonces protégeraient contre les attaques par rejeu. Les nonces sont des "numéros utilisés une seule fois". Il suffit donc de conserver une liste des nonces utilisés précédemment et de refuser toute tentative d'utiliser un nonce deux fois.

2 votes

Downvoted parce que la bibliothèque liée, FT-Nonce, est extrêmement peu sûre dans sa génération de clés uniques.

31voto

Scott Points 389

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 :

  1. 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 ).
  2. 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.
  3. 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 :

  1. Lesquelles de ces écoles de pensée sont les plus pertinentes pour le problème que vous essayez de résoudre ?
  2. Comment générez-vous le nonce ?
  3. 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 .

2voto

Maurycy Zarzycki Points 884

Une option (que j'ai mentionnée dans le commentaire) est d'enregistrer le jeu et de le rejouer dans un environnement sécurisé.

L'autre chose est d'enregistrer aléatoirement, ou à des moments précis, des données apparemment innocentes, qui pourront être utilisées plus tard pour valider la tricherie sur le serveur (par exemple, un live qui passe soudainement de 1 % à 100 %, ou un score de 1 à 1000 qui indique une tricherie). Avec suffisamment de données, il pourrait être impossible pour un tricheur d'essayer de tricher. Et ensuite, bien sûr, mettre en place des bannissements lourds :).

1voto

oleviolin Points 487

Ce nonce très simple change toutes les 1000 secondes (16 minutes). et peut être utilisé pour éviter les XSS lorsque vous envoyez des données vers et depuis la même application. (Par exemple, si vous êtes dans une application à page unique où vous envoyez des données via javascript. Notez que vous devez avoir accès à la même graine et au même générateur de nonce du côté de l'envoi et de la réception).

function makeNonce($seed,$i=0){
    $timestamp = time();
    $q=-3; 
    //The epoch time stamp is truncated by $q chars, 
    //making the algorthim to change evry 1000 seconds
    //using q=-4; will give 10000 seconds= 2 hours 46 minutes usable time

    $TimeReduced=substr($timestamp,0,$q)-$i; 

    //the $seed is a constant string added to the string before hashing.    
    $string=$seed.$TimeReduced;
    $hash=hash('sha1', $string, false);
    return  $hash;
}   

Mais en vérifiant le nonce précédent, l'utilisateur ne sera dérangé que s'il a attendu plus de 16,6 minutes dans le pire des cas et 33 minutes dans le meilleur des cas. En fixant $q=-4, l'utilisateur pourra attendre au moins 2,7 heures.

function checkNonce($nonce,$seed){
//Note that the previous nonce is also checked giving  between 
// useful interval $t: 1*$qInterval < $t < 2* $qInterval where qInterval is the time deterimined by $q: 
//$q=-2: 100 seconds, $q=-3 1000 seconds, $q=-4 10000 seconds, etc.
    if($nonce==$this->makeNonce($seed,0)||$nonce==$this->makeNonce($seed,1))     {
         //handle data here
         return true;
     } else {
         //reject nonce code   
         return false;
     }
}

Le $seed peut être un appel de fonction, un nom d'utilisateur, etc. utilisé dans le processus.

0voto

Timo Stamm Points 31

Il n'est pas possible d'empêcher la tricherie. On peut seulement la rendre plus difficile.

Si quelqu'un venait ici à la recherche d'une bibliothèque PHP Nonce : Je recommande de ne pas utiliser le premier donné par ircmaxwell .

Le premier commentaire sur le site web décrit un défaut de conception :

Le nonce est valable pour une certaine fenêtre temporelle, c'est-à-dire que plus l'utilisateur se rapproche de l'endroit où il se trouve, plus le nonce est efficace. l'utilisateur se rapproche de la fin de cette fenêtre, il dispose de moins de temps pour soumettre le formulaire. soumettre le formulaire, peut-être moins d'une seconde

Si vous cherchez un moyen de générer des Nonces avec une durée de vie bien définie, jetez un coup d'oeil à NonceUtil-PHP .

Disclaimer : Je suis l'auteur de NonceUtil-PHP

0 votes

Pouvez-vous confirmer que cet utilitaire, rédigé il y a plus d'un an, est toujours un bon choix ?

5 votes

@NathanArthur si vous vérifiez l'auteur, c'est Timo lui-même. Son NonceUtil.php utilise sha-1, ne vérifie pas les nonces précédents et a une chaîne codée en dur pour le sel... La première bibliothèque listée dans la réponse d'ircmaxwell utilise md5 et la seconde utilise un simple rand pour créer le nonce. C'est-à-dire qu'aucune n'utilise sha256/sha512. Cependant, si le but du nonce est juste d'avoir une chaîne non répétable, alors vous avez juste besoin d'un bon générateur aléatoire (par exemple : openssl_random_pseudo_bytes ), puis de stocker ceux qui ont été générés afin de les vérifier avant de les utiliser.

1 votes

Je ne recommanderais pas NonceUtil tel qu'il est actuellement implémenté.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X