Libsodium pour les nuls, ou la cryptographie en PHP
(Read the đŹđ§ version here)
La cryptographie, plus communĂ©ment appelĂ© crypto, est une des disciplines de la cryptologie sâattachant Ă protĂ©ger des messages (assurant confidentialitĂ©, authenticitĂ© et intĂ©gritĂ©) en sâaidant souvent de secrets ou clĂ©s.
Avez-vous déjà eu besoin :
- de protĂ©ger lâaccĂšs Ă une ressource sans pour autant stocker dâinformation en baseâŻ?
- de stocker des informations sensibles de maniÚre sécurisée�
- de vĂ©rifier lâauthenticitĂ© dâun messageâŻ?
AprÚs la lecture de cet article, vous saurez comment résoudre ces problématiques de maniÚre sécurisée.
Section intitulĂ©e qu-est-ce-que-libsodiumQuâest ce que libsodiumâŻ?
Libsodium est une librairie Ă©crite en C pour faire de la cryptographie. Elle est moderne (v1 en 2014), portable, Ă©crite par Frank Denis đ«đ· et surtout trĂšs simple dâutilisation.
Lâextension libsodium est packagĂ©e par dĂ©faut depuis PHP 7.2. Pour les versions de PHP antĂ©rieures Ă 7.2, il faudra installer une extension packagĂ©e en PECL. Ă la place de la PECL, il est aussi possible dâutiliser le polyfill sodium_compat.
Lâextension libsodium va exposer une sĂ©rie de fonctions dans PHP afin de faciliter nos usages cryptographiques. Vous pouvez vĂ©rifier que lâextension est bien installĂ©e grĂące Ă php --re sodium qui va lister toutes les fonctions, classes, et constantes dĂ©finies par lâextension.
Section intitulée prerequi-a-la-crypto-generer-des-valeurs-aleatoiresPrérequi à la crypto : Générer des valeurs aléatoires
La gĂ©nĂ©ration de valeurs alĂ©atoires est quelque chose de trĂšs compliquĂ© en informatique. Ă tel point que certaines entreprises comme Cloudflare utilisent des LavaLamp pour en gĂ©nĂ©rer. La gĂ©nĂ©ration de valeurs est primordiale lorsque lâon fait de la crypto. Un gĂ©nĂ©rateur de nombres (pseudo) alĂ©atoires est ce que lâon appelle un Cryptographically Secure Pseudo-Random Number Generator (CSPRNG).
libsodium expose plusieurs fonctions pour en gĂ©nĂ©rer. Cependant depuis PHP 7.0, il existe deux nouvelles fonctions random_bytes() et random_int() qui ont un niveau pseudo alĂ©atoire acceptable. Câest pourquoi ces fonctions nâont pas Ă©tĂ© portĂ©es lors de lâintĂ©gration de libsodium Ă PHP 7.2.
Section intitulĂ©e des-cas-d-usagesDes cas dâusagesâŻ?
à travers une série de use cases concrets, nous allons voir comment utiliser cette extension.
Section intitulĂ©e proteger-l-acces-a-une-ressourceProtĂ©ger lâaccĂšs Ă une ressource
Prenons lâexemple dâun site web qui affiche la position gĂ©ographique dâun arrĂȘt de bus sur une carte. Le fournisseur des cartes (mapbox, google maps, âŠ) nous propose une API, mais cette API est authentifiĂ©e et rate limitĂ©e (nous ne pouvons lâappeler que X fois par jour).
Nous avons besoin de protĂ©ger notre point dâentrĂ©e dâAPI qui retourne une carte en fonction de certains paramĂštres. Nous pourrions stocker en base de donnĂ©es toutes les possibilitĂ©s, mais cela nĂ©cessitera du code, de la maintenance, et de la place en base de donnĂ©es.
Une alternative possible consiste Ă signer les requĂȘtes :
- le serveur génÚre du HTML contenant une balise image :
<img src="/image-map?lat=xxx&long=yyy&signature=zzzz"> - le navigateur demande lâimage au serveurâŻ;
- le serveur va vérifier que la signature de
xxxetyyyest bienzzzzâŻ; - le serveur va chercher lâimage chez le fournisseur de cartesâŻ;
- le serveur renvoie lâimageâŻ;
Il est maintenant impossible de demander une carte qui nâa pas Ă©tĂ© Ă©mise par le serveur. Autrement dit, personne ne peut utiliser vos credentials dâAPI pour gĂ©nĂ©rer ses propres cartes quâil utilisera sur son site.
Il faut commencer par gĂ©nĂ©rer une clĂ© quâil faudra bien garder au chaud et ne jamais perdre :
$key = sodium_crypto_generichash_keygen();
Puis il faut générer une signature pour une carte :
// Construire un message que l'on va signer
$msg = "lat=XXXX;long=YYYY";
// Générer la signature
$signature = sodium_crypto_generichash($msg, $key);
$signatureAsTxt = bin2hex($signature);
dump(['signature' => $signature, 'signatureAsTxt' => $signatureAsTxt]);
Ce qui va nous afficher quelque chose comme :
array:2 [
"signature" => b"\x19ĂOSĂĂœ\x11YĂĂ>Bâ©Ă»ĂrĂ”l\x02>^n b>ÂȘ©[ĂĂŹĂ"
"signatureAsTxt" => "19a14f5390ec1159e19a3e42ca96d172e46c023e5e6eb6623ea6b8b25b998dd4"
]
que nous pourrons envoyer au client :
<img src="/image-map?lat=XXXX&long=YYYY&signature=19a14f5390ec1159e19a3e42ca96d172e46c023e5e6eb6623ea6b8b25b998dd4">
Lorsque une requĂȘte HTTP arrive, nous allons refaire le mĂȘme processus puis vĂ©rifier que les deux signatures sont identiques :
$lat = $_GET['lat'];
$long = $_GET['long'];
$signatureAsTxt2 = $_GET['signature'];
$msg2 = "lat=$lat;long=$long";
$signature2 = sodium_crypto_generichash($msg2, $key);
// Nous vérifions les deux signatures :
$ok = hash_equals($signature2, hex2bin($signatureAsTxt2));
if (!$ok) {
throw new \Exception('Cette requĂȘte n\'est pas valide');
}
La gĂ©nĂ©ration du message ($msg) peut ĂȘtre une tĂąche fastidieuse sâil y a beaucoup de paramĂštres. Dans ce cas, il sera plus simple dâutiliser les fonctions sodium_crypto_generichash_*() :
$hashState = sodium_crypto_generichash_init();
sodium_crypto_generichash_update($hashState, 'XXXX');
sodium_crypto_generichash_update($hashState, 'YYYY');
$msg = sodium_crypto_generichash_final($hashState);
//...
Section intitulée stocker-des-informations-dans-un-fichier-via-un-mot-de-passeStocker des informations dans un fichier via un mot de passe
Prenons lâexemple dâun projet oĂč certains dĂ©veloppeurs peuvent se connecter via SSH aux serveurs de production. Vous ne voulez pas avoir un fichier avec tous les mots de passe sur votre rĂ©seau (ou chez Google Drive, Dropbox, Github, âŠ) en clair, et vous avez raison. Cependant, stocker un fichier chiffrĂ© dans le dĂ©pĂŽt Git que lâon ne peut « ouvrir » quâavec un mot de passe vous convientâŻ? Alors cette section est faite pour vous.
Il faut commencer par gĂ©nĂ©rer une clĂ© quâil faudra bien garder au chaud et ne jamais perdre :
$key = sodium_crypto_secretbox_keygen();
Ensuite il faut gĂ©nĂ©rer un nonce. Contrairement Ă la clĂ©, le nonce nâa pas besoin dâĂȘtre privĂ©. Nous pourrons le sauvegarder Ă cĂŽtĂ© de la valeur chiffrĂ©e.
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
Et enfin, nous pouvons chiffrer le message :
$encrypted = sodium_crypto_secretbox('my super secret data', $nonce, $key);
Et pour déchiffrer le message chiffré :
$decrypted = sodium_crypto_secretbox_open($encrypted, $nonce, $key);
Section intitulée authentifier-un-documentAuthentifier un document
Pour ce cas dâusage, prenons lâexemple du serveur qui gĂ©nĂšre des factures au format PDF. Il est assez facile de modifier un PDF, et donc vous voulez garantir lâintĂ©gritĂ© du document et de son contenu. ConcrĂštement, vous ne voulez pas que quelquâun de mal intentionnĂ© fabrique de fausses factures qui auraient Ă©tĂ© Ă©mises par votre site web. Vous voulez Ă©galement que nâimporte qui puisse valider que le document a bien Ă©tĂ© Ă©mis par votre site web. Par exemple, quâune assurance puisse vĂ©rifier quâun client a bel et bien achetĂ© tel ou tel article.
GrĂące Ă ce systĂšme, les assureurs nâauront pas besoin de communiquer avec vous pour authentifier la facture.
La premiĂšre Ă©tape consiste en la gĂ©nĂ©ration dâune clĂ© quâil faudra conserver secrĂštement :
$keyPair = sodium_crypto_sign_keypair();
à partir de cette clé, nous allons extraire une partie privée qui va nous servir à signer des documents et une partie publique que nous pourrons diffuser à tout le monde :
$keyPairSecret = sodium_crypto_sign_secretkey($keyPair);
$keyPairPublic = sodium_crypto_sign_publickey($keyPair);
Grùce à cette clé, nous pouvons maintenant signer le document :
$message = 'Hello';
$signature = sodium_crypto_sign_detached($message, $keyPairSecret);
Maintenant, nous pouvons envoyer le document avec sa signature au client. Celui-ci et lâassureur pourront authentifier le document de la maniĂšre suivante :
$ok = sodium_crypto_sign_verify_detached($signature, $message, $keyPairPublic);
if (!$ok) {
throw new \Exception('Le document a été modifié');
}
Section intitulée envoyer-un-message-super-securise-a-un-utilisateurEnvoyer un message super sécurisé à un utilisateur
Dans certains cas, nous voulons un niveau de sĂ©curitĂ© maximal oĂč mĂȘme HTTPS ne suffit pas. HTTPS peut ĂȘtre compromis Ă diffĂ©rents niveaux : une faille dans SSL, une entreprise qui fait un Man In The Middle, etc⊠Pour palier ce problĂšme, il est possible de gĂ©nĂ©rer un message que seule une personne pourra lire. Pour ce faire, nous allons utiliser un chiffrement asymĂ©trique (comme dans le chapitre prĂ©cĂ©dent) mais avec plus de clĂ©s.
Un cas concret dâutilisation est un client mail, oĂč la sĂ©curitĂ© doit ĂȘtre maximale.
Le principe est le suivant :
- Alice crĂ©e une paire de clĂ©s asymĂ©triques (publique et privĂ©e)âŻ;
- Bob crĂ©e une paire de clĂ©s asymĂ©triques (publique et privĂ©e)âŻ;
- Ils sâĂ©changent leurs clĂ©s publiquesâŻ;
- Alice chiffre son message avec sa clĂ© privĂ©e puis avec la clĂ© publique de BobâŻ;
- Alice envoie alors le message chiffrĂ© Ă BobâŻ;
- Bob reçoit le message chiffrĂ© dâAliceâŻ;
- Bob va alors dĂ©chiffrer le message avec sa clĂ© privĂ©e, puis avec la clĂ© publique dâAlice.
GrĂące Ă ce double chiffrement :
- Le message nâest dĂ©chiffrable que par BobâŻ;
- MĂȘme Alice ne peut pas dĂ©chiffrer le message quâelle a elle-mĂȘme chiffrĂ©âŻ;
- Bob peut sâassurer que ce soit Alice qui a Ă©mis le message.
Pour réaliser ceci, il va falloir exécuter les étapes suivantes :
Sur lâordinateur dâAlice (en gĂ©nĂ©ral, un serveur) :
// Cette clé est à garder secrÚte
$aliceKey = sodium_crypto_box_keypair();
$aliceKeySecret = sodium_crypto_box_secretkey($aliceKey);
// Cette clé est diffusée à Bob
$aliceKeyPublic = sodium_crypto_box_publickey($aliceKey);
Sur lâordinateur de Bob (en gĂ©nĂ©ral, en Javascript dans le navigateur, ou sur un autre serveur dans le cas dâAPI) :
// Cette clé est à garder secrÚte
$bobKey = sodium_crypto_box_keypair();
$bobKeySecret = sodium_crypto_box_secretkey($bobKey);
// Cette clé est diffusée à Alice
$bobKeyPublic = sodium_crypto_box_publickey($bobKey);
Alice va chiffrer son message et lâenvoyer Ă Bob :
$message = 'hello';
$nonce = random_bytes(SODIUM_CRYPTO_BOX_NONCEBYTES);
$encryptionKey = sodium_crypto_box_keypair_from_secretkey_and_publickey($aliceKeySecret, $bobKeyPublic);
$encrypted = sodium_crypto_box($message, $nonce, $encryptionKey);
Bob nâa plus quâĂ dĂ©chiffrer le message dâAlice :
$decryptionKey = sodium_crypto_box_keypair_from_secretkey_and_publickey($bobKeySecret, $aliceKeyPublic);
$decrypted = sodium_crypto_box_open($encrypted, $nonce, $decryptionKey);
Section intitulée utilitairesUtilitaires
libsodium expose aussi quelques utilitaires qui sont plus sécurisés que les fonctions natives de PHP. En effet, libsodium est moins sensible au Side Channel Attack :
sodium_bin2hex()/sodium_hex2bin(): Conversion binaire <-> hexaâŻ;sodium_bin2base64()/sodium_base642bin(): Conversion binaire <-> base64âŻ;sodium_increment(): IncrĂ©mente un nombre (trĂšs grand);sodium_compare()/sodium_memcmp(): Compare deux valeurs en temps constant. Cependant depuis PHP 5.6hash_equals()est suffisantâŻ;sodium_pad()/sodium_unpad(): Permet de complĂ©ter une string par des 0 pour obtenir une taille fixe. Cela permet dâĂ©viter Ă un attaquant de trouver la taille du message.sodium_memzero(): Permet de supprimer la valeur dâune variable de la mĂ©moire.
Cependant, ne vous inquiétez pas, les fonctions de base de PHP sont trÚs bien. PHP, de part sa nature (langage pour faire du web), est peu sensible aux SCA.
Section intitulée conclusionConclusion
Lâextension libsodium est native Ă PHP depuis la 7.2. Elle est Ă connaĂźtre dĂšs que lâon veut amĂ©liorer la sĂ©curitĂ© de son application. Son API est simple et ne nĂ©cessite pas de grosses connaissances en cryptographie. Cependant, elle est trĂšs mal documentĂ©e sur php.net. Il ne faut pas hĂ©siter Ă aller voir la documentation de la lib C sous-jacente qui est assez complĂšte et pleine dâexemples.
Enfin, Paragonie, une entreprise spĂ©cialisĂ©e dans la sĂ©curitĂ© informatique, a publiĂ© halite, une surcouche Ă libsodium. Son but est de simplifier lâutilisation de libsodium, de mettre en avant les bonnes pratiques, et dâajouter encore une couche de sĂ©curitĂ©.
Commentaires et discussions
Nos articles sur le mĂȘme sujet
What libsodium can do for you? An Introduction to Cryptography in PHP
(Read the đ«đ· version here) Cryptography or cryptology is the practice and study of techniques for secure communication in the presence of third parties called adversaries. More generally, cryptography is aboutâŠ
par Grégoire Pineau
Nos formations sur ce sujet
Notre expertise est aussi disponible sous forme de formations professionnellesâŻ!
Symfony avancée
DĂ©couÂvrez les foncÂtionÂnaÂliÂtĂ©s et concepts avanÂcĂ©s de SymfoÂny
Ces clients ont profité de notre expertise
Groupama Ăpargne Salariale digitalise son expĂ©rience client en leur permettant dâeffectuer leurs versements dâĂ©pargne salariale en ligne. Lâapplication offre aux entreprises une interface web claire et dynamique, composĂ© dâun tunnel de versement complet : import des salariĂ©s via fichier Excel, rappel des contrats souscrits et des plans disponibles, âŠ
Au fil de notre collaboration avec Deezer, nous avons Ă©tĂ© impliquĂ©s dans plusieurs initiatives visant Ă optimiser les performances de leur plateforme. Notre engagement initial sâest concentrĂ© sur le soutien et le conseil Ă lâĂ©quipe « Product Features » lors de leur projet de migration en cours. Nous avons apportĂ© notre expertise pour rĂ©soudreâŠ
Nous avons entrepris une refonte complĂšte du site, initialement dĂ©veloppĂ© sur Drupal, dans le but de le consolider et de jeter les bases dâun avenir solide en adoptant Symfony. La plateforme est hautement sophistiquĂ©e et propose une plĂ©thore de fonctionnalitĂ©s, telles que la gestion des abonnements avec Stripe et Paypal, une API pour lâapplicationâŠ