Accéder au contenu principal

8min.

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.

via wikipedia

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 :

  1. le serveur gĂ©nĂšre du HTML contenant une balise image :
    <img src="/image-map?lat=xxx&long=yyy&signature=zzzz">
  2. le navigateur demande l’image au serveur ;
  3. le serveur va vĂ©rifier que la signature de xxx et yyy est bien zzzz ;
  4. le serveur va chercher l’image chez le fournisseur de cartes ;
  5. 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 :

  1. Alice crĂ©e une paire de clĂ©s asymĂ©triques (publique et privĂ©e) ;
  2. Bob crĂ©e une paire de clĂ©s asymĂ©triques (publique et privĂ©e) ;
  3. Ils s’échangent leurs clĂ©s publiques ;
  4. Alice chiffre son message avec sa clĂ© privĂ©e puis avec la clĂ© publique de Bob ;
  5. Alice envoie alors le message chiffrĂ© Ă  Bob ;
  6. Bob reçoit le message chiffrĂ© d’Alice ;
  7. 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.6 hash_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

Nos formations sur ce sujet

Notre expertise est aussi disponible sous forme de formations professionnelles !

Voir toutes nos formations

Ces clients ont profité de notre expertise