web-dev-qa-db-fra.com

Comment chiffrer correctement en PHP?

Comment chiffrer correctement les données en PHP à l'aide du chiffrement à clé symétrique? J'ai un message M et un secret S. Je recherche une solution qui utilise correctement la cryptographie sans commettre les erreurs habituelles. En particulier, la solution doit utiliser un cryptage authentifié, choisir correctement les IV et générer la clé de cryptage réelle à partir du S secret en utilisant un hachage lent approprié (au cas où S se trouve être un mot de passe au lieu d'une clé cryptographique réelle).

Pouvez-vous suggérer PHP code à cet effet?


Mon motif: je voudrais donner PHP bons conseils sur la façon de faire, pas de mauvais conseils. Cette question sur StackOverflow est une vraie déception: c'est plein de réponses très positives qui sont terriblement mauvaises (cryptage en mode ECB? répétition IVs? cryptage sans authentification?). Voyons la bonne réponse - un extrait de code qui fait les choses correctement - et ensuite réparer cette fenêtre cassée sur StackOverflow. Conformément à la philosophie du programmeur PHP, je préférerais un extrait de code (qui fonctionne pour autant de paramètres possibles; éventuellement avec explication et/ou explication des limitations/mises en garde) au lieu de simplement des conseils sur les algorithmes et les concepts.

37
D.W.

Pouvez-vous suggérer PHP code à cet effet?

Dans l'ordre de préférence.

1. PECL Libsodium

Si vous n'avez pas entendu parler de libsodium, allez en savoir plus maintenant . Je très fortement vous recommande d'utiliser libsodium si possible.

Ces instructions devrait vous aider à installer libsodium et l'extension PECL sur votre ordinateur. Le reste de ce livre électronique devrait vous expliquer comment utiliser des composants spécifiques et contient de nombreux exemples de code. Si vous avez besoin d'un point de départ pour la cryptographie PHP vers laquelle pointez les non-experts, libsodium est le meilleur disponible aujourd'hui.

Pour le chiffrement authentifié par clé secrète, reportez-vous à ce chapitre du livre électronique .

2. paragonie/halite

(Avertissement: j'ai écrit Halite.)

Toute la puissance de libsodium et une interface qui ressemble à:

use \ParagonIE\Halite\Symmetric\Crypto as Symmetric;

$key = KeyFactory::loadEncryptionKey('/path/to/key/file');
$ciphertext = Symmetric::encrypt($plaintext, $key);

Découvrez-le: paragonie/halite .

3. désamorçage/cryptage php

Defuse Security a publié une bibliothèque de cryptage * sur Github à defuse/php-encryption .

Par cryptage, je veux dire cryptage authentifié :

  • Version 1:
    • AES-128-CBC avec rembourrage PKCS # 7 et IV aléatoire
    • HMAC-SHA-256 (vérifié en temps constant)
    • Utilise HKDF-SHA256 pour diviser votre clé en une clé de cryptage et une clé d'authentification
  • Version 2:
    • AES-256-CTR avec un nonce aléatoire (le mode CTR n'a pas besoin de remplissage)
    • HMAC-SHA-256 (vérifié en temps constant)
    • Utilise HKDF-SHA256 pour diviser votre clé en une clé de cryptage et une clé d'authentification; maintenant avec un sel HKDF aléatoire pour atténuer l'impact des collisions d'anniversaire dans le CTR aléatoire après avoir chiffré 2 ^ 64 blocs

Avertissement: je suis l'un des co-auteurs de cette bibliothèque, bien que la plupart de mes contributions n'atterriront pas avant la réécriture de la version 2, qui comprend le balisage de version et une interface de streaming pour crypter/décrypter les fichiers en toute sécurité.

4. zend/zend-crypt

Je recommande celui-ci dernier parce que je ne l'ai pas personnellement audité, mais il c'est une partie du Framework Zend et beaucoup de PHP = des experts en sécurité attestent de sa qualité. Allez-y et utilisez-le si vous utilisez déjà Zend Framework.

Mise à jour : depuis que j'ai écrit cette réponse à l'origine, j'ai découvert un problème dans Zend\Crypt\PublicKey\Rsa ( ZF2015-10 alias CVE-2015-75 ). Cependant, leur implémentation de chiffrement à clé symétrique semble être sécurisée.

16
Scott Arciszewski

D.W., la réponse doit venir plus sous la forme d'un modèle de conception que d'un extrait de code. L'une des raisons en est l'état de la technique avec PHP et la communauté PHP. Il existe de précieux exemples de "faire les choses correctement" qui sont disponibles pour moyenne PHP développeurs.

Exemples de cette difficulté:

  1. Si vous tapez mcrypt dans votre code, vous vous trompez : mycrypt, comme la dernière fois que j'ai regardé, n'est plus maintenu et est effectivement abandonné. Cependant, mycrypt est l'extension capable d'obtenir des nombres aléatoires à partir de/dev/urandom, ce qui semble être la bonne façon de le faire sur des serveurs Linux.

  2. Comment générer en toute sécurité un nombre aléatoire : l'extension openssl de PHP se trompe. Openssl est l'extension à utiliser pour les nouveaux développements, mais elle ne fonctionne pas correctement.

Cela nous amène à la conclusion que nous devons créer notre propre code de cryptographie. Tout expert nous dira: "Ne fais pas ça!" Mais avec PHP il n'y a peut-être pas d'alternative. Il existe des packages de cryptographie fournis par la communauté, mais c'est le même problème: comment savoir si ce package "réussit?"

L'autre raison est qu'avec PHP, la question se pose à cause d'une situation spécifique. Essayez-vous de stocker des mots de passe hachés sur un réseau non sécurisé? Un ensemble de protocoles pourrait être préférable.

Essayez-vous de sécuriser les services Web entre votre serveur et votre application mobile? Une approche différente peut être plus appropriée. (Par exemple, pouvez-vous dépendre que l'application garde une clé secrète secrète, lorsque l'application peut être rétroconçue par un attaquant?)

Pour mémoire, voici les étapes que j'ai envisagées avec le cryptage à clé secrète symétrique. J'utilise PHP 5.3, l'extension mcrypt pour l'accès à/dev/urandom et openssl pour aes-256-cbc.

  1. Reconnaissez que tout dépend de la confidentialité de la clé secrète. Si les clés secrètes peuvent être extraites du logiciel client distant (ou de l'un des points d'extrémité de votre communication), un attaquant ayant accès au texte chiffré peut lire tout et n'importe quoi, et insérer des messages parasites.

  2. Reconnaissez que la transmission via HTTPS, par exemple, n'offre pas une sécurité suffisante contre certaines attaques. Par exemple, un attaquant peut installer notre application et observer le trafic HTTPS déchiffré envoyé/reçu vers/depuis notre application.

  3. En d'autres termes, comprenez l'environnement dans lequel fonctionne votre cryptage et découvrez quelles attaques doivent être prises en compte.

  4. Les deux côtés du chiffrement ont besoin de la même clé secrète partagée. Les deux parties doivent obtenir la clé, et les deux doivent empêcher la clé d'être volée, vue, rétroconçue, etc.

  5. Je crois (mais manque d'expertise suffisante pour être sûr) que c'est la bonne pratique de hacher votre clé secrète à exactement 256 bits (pour AES-256), par exemple, utiliser du hachage ('sha256', $ secretKey) pour créer la clé 256 bits pour alimenter la fonction de chiffrement/déchiffrement. En d'autres termes, le simple ASCII texte en l'état n'a pas assez d'entropie pour être directement utilisé comme clé secrète. Donc, utilisez une fonction de hachage créant (ou étant tronquée) le nombre correct d'octets.

  6. Pour AES, vous avez besoin d'un vecteur d'entrée 128 bits. Comprenez que ce vecteur d'entrée doit être différent pour chaque cryptage/transmission. J'ai vu trop d'échantillons de code avec un vecteur d'entrée fixe! Utilisez mcrypt avec le mode MCRYPT_DEV_URANDOM pour les octets aléatoires.

  7. The Doom Principle * thanks @ mti2935) indique que, si vous effectuez N'IMPORTE QUELLE opération cryptographique sur le message reçu avant de vérifier son intégrité, cela ENTRAÎNERAIT forcément Doom. Une meilleure pratique consiste à utiliser le HMAC (le point suivant).

  8. (pas assez de réputation pour publier le lien): Comprenez que le chiffrement n'est pas l'authentification. Le HMAC peut uniquement vérifier que le message n'a pas été modifié. Il vérifie également que le message a été envoyé par une personne possédant votre clé secrète. Vous pouvez ou non considérer ce fait comme une authentification suffisante.

  9. Le HMAC doit couvrir TOUTES les entrées de votre fonction de déchiffrement. Cela signifie à la fois le message crypté ET le vecteur d'entrée. Si vous avez un identifiant indiquant quel algorithme de déchiffrement utiliser, cela doit également faire partie du calcul HMAC. Sinon, l'attaquant pourrait modifier cet indicateur et monter certaines attaques, et votre validation HMAC apparaîtra toujours propre.

  10. Ce qui précède est l'école de pensée "encrypt-then-HMAC". Il existe des écoles de pensée opposées, notamment Bruce Schneier. Le raisonnement de Bruce est qu'il est particulièrement difficile d'obtenir le droit de "chiffrer puis HMAC". Si Bruce dit que c'est particulièrement difficile, c'est particulièrement difficile.

Mon point est que si vous chiffrez et HMAC, dans n'importe quel ordre, DÉCOUVREZ ce qui est si difficile à faire correctement, et obtenez l'accord que vous avez bien fait.

1
Edward Barnard

Écrire le code cryptographique ne le publie pas Après réflexion, il vaut peut-être mieux que je n'expose pas le code roll-your-own comme exemple. Utilisez libsodium.

1
Edward Barnard