Lesson 12/14 12 min

Applied cryptography

Encode, hash, encrypt: three words people confuse, and secrets leak because of it. Break a fake protection yourself, then real crypto: HTTPS, libsodium, and "never roll your own crypto".

« Je l'ai encodé, donc c'est protégé »

La leçon 11 a vu HSTS forcer le HTTPS sur toute la connexion. Mais encore faut-il que ce HTTPS s'appuie sur une vraie crypto, pas sur un base64 pris pour du chiffrement. C'est exactement le piège dans lequel tombe ce dev :

Il veut protéger les clés d'API dans son fichier de config. Il les passe en base64, voit une bouillie illisible, et se dit que c'est sécurisé :

API_KEY=c2stbGl2ZS04YTdkM2UtcHJvZA==

Un attaquant qui lit le fichier tape une commande, une seule :

echo 'c2stbGl2ZS04YTdkM2UtcHJvZA==' | base64 -d
sk-live-8a7d3e-prod

En une seconde, la clé est en clair. Le base64 n'est pas du chiffrement : c'est juste une autre façon d'écrire les mêmes octets. N'importe qui peut le relire, sans aucune clé. Cette confusion entre encoder, hacher et chiffrer est à l'origine d'innombrables fuites. C'est lié à l'OWASP 2025 (A04 Cryptographic Failures).

Encoder, hacher, chiffrer : ne jamais confondre

Trois opérations, trois buts différents. Les mélanger, c'est créer la faille.

  • Encoder (base64, URL-encoding) : changer la forme d'une donnée pour la transporter. Réversible par tous, sans clé. Ça ne protège rien. C'est juste un autre alphabet.
  • Hacher (SHA-256, bcrypt) : transformer une donnée en empreinte, à sens unique. On ne revient jamais en arrière. Sert à vérifier l'intégrité et à stocker les mots de passe (leçon 7).
  • Chiffrer (AES, RSA) : rendre une donnée illisible, réversible avec une clé. Sert à protéger un secret qu'on devra relire. Sans la clé, illisible.

La règle tient en une ligne : encoder pour transporter, hacher pour vérifier ou stocker un mot de passe, chiffrer pour protéger un secret qu'on relira. Le base64 du début n'était ni un hachage ni un chiffrement : juste un déguisement, retiré en une commande.

Encoder, hacher, chiffrer : trois opérations, trois buts Encoder (base64) : réversible par tous, sans clé Hacher (bcrypt) : à sens unique, irréversible Chiffrer (AES) : réversible, mais seulement avec la clé
Encoder ne protège rien (gris). Hacher et chiffrer protègent (vert), mais différemment : l'un est à sens unique, l'autre se déchiffre avec la clé.

Le faux remède : « je l'ai encodé en base64 » ou « j'ai écrit mon petit algo maison » ne protège rien. L'encodage se décode, et un algo bricolé se casse en minutes. La sécurité ne vient pas du secret de la méthode, mais d'une vraie crypto avec une vraie clé (section 4).

À vous d'attaquer : cassez la fausse protection

Vous avez volé un secret « protégé ». Choisissez la méthode de protection, puis essayez de le casser sans la clé. Vous verrez tomber le base64 et la crypto maison en un clic, et l'AES-256 résister. Tout est calculé dans votre navigateur.

Secret volé · forme « protégée »

        
        

    
Ce qui vient de se passer

base64 se décode avec une fonction standard (atob, base64 -d) : aucune clé, aucune protection. La crypto maison (XOR à clé courte) se casse presque aussi vite : la clé se devine ou se force, et le motif se voit. Les deux n'étaient que de l'obscurité.

AES-256, lui, résiste : sans la clé, il faudrait essayer 2256 possibilités, plus que d'atomes sur Terre. C'est ça, du vrai chiffrement : la sécurité tient à la clé, pas au secret de l'algorithme (qui, lui, est public et étudié par le monde entier).

Le correctif : la vraie crypto, et la règle d'or

D'abord deux familles de chiffrement. Le symétrique (AES) utilise une seule clé partagée, rapide. L'asymétrique (RSA, courbes elliptiques) utilise une paire : une clé publique pour chiffrer, une clé privée pour déchiffrer. Cela résout le partage de la clé. En pratique, HTTPS combine les deux : l'asymétrique sert à échanger une clé symétrique, puis le symétrique fait le gros du travail, plus vite.

Chiffrement symétrique : une seule clé partagée. Asymétrique : une clé publique pour chiffrer, une clé privée pour déchiffrer. Symétrique (AES) : la même clé chiffre et déchiffre Message Chiffré Message clé K clé K Asymétrique (RSA) : deux clés différentes, une paire Message Chiffré Message clé publique clé privée
Symétrique : une seule clé secrète, rapide, mais il faut réussir à la partager. Asymétrique : on chiffre avec la clé publique, on déchiffre avec la clé privée, ce qui règle le partage.

Côté code, on n'écrit pas la crypto soi-même : on appelle une bibliothèque éprouvée. En PHP, libsodium est intégré et fait du chiffrement authentifié (chiffré + protégé contre la modification) :

$cle    = sodium_crypto_secretbox_keygen();                         // clé aléatoire (gardée à part)
$nonce  = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);         // nonce unique à chaque fois
$chiffre = sodium_crypto_secretbox($message, $nonce, $cle);         // chiffré + authentifié
$clair   = sodium_crypto_secretbox_open($chiffre, $nonce, $cle);    // déchiffré

Deux autres réflexes essentiels. HTTPS/TLS chiffre tout ce qui transite (cookies, mots de passe) : sans lui, tout passe en clair sur le réseau (leçon 8). Le certificat est gratuit (Let's Encrypt), et on ajoute HSTS (leçon 11). Et les secrets (clés, mots de passe de base) ne vivent jamais en dur dans le code : ils finiraient dans l'historique git. Mettez-les ailleurs : des variables d'environnement, un fichier .env non versionné et hors du dossier web, ou un gestionnaire de secrets.

La règle d'or : n'inventez jamais votre propre algorithme de chiffrement. Un bon algorithme est public et résiste quand même (principe de Kerckhoffs) : sa solidité vient de la clé, pas du secret du code. Les algos standards ont déjà encaissé des années d'attaques ; le vôtre repartirait de zéro, sans cette épreuve du feu. Utilisez AES-GCM ou libsodium, jamais le mode ECB (qui laisse voir les motifs), et ne réutilisez jamais un nonce.

Défense en profondeur :

  1. le bon outil : encoder pour transporter, hacher pour les mots de passe (bcrypt/Argon2, leçon 7), chiffrer pour un secret relisible ;
  2. une bibliothèque éprouvée (libsodium), du chiffrement authentifié, jamais ECB, nonce unique ;
  3. HTTPS partout (+ HSTS), certificat Let's Encrypt ;
  4. les secrets hors du code : .env non versionné, gestionnaire de secrets, rotation.

Référence : OWASP Cryptographic Storage Cheat Sheet.

La méthode et l'arsenal du pentester

On vient de voir comment se défendre. Voilà maintenant comment un attaquant lit exactement ce que vous croyez avoir caché.

1. Repérer la fausse protection. Une chaîne qui finit par == sent le base64 ; eyJ... est un JWT (JSON Web Token, un jeton d'authentification encodé en base64, donc lisible sans aucune clé). Un cookie « chiffré » qui se décode tout seul, un token « secret » qui n'est que de l'encodage : on le voit vite.

2. Identifier l'algorithme. La longueur trahit le hash : 32 caractères hexa = md5, 40 = sha1, 64 = sha256. Le format (hexa, base64) et le mode donnent le reste.

3. Casser le faible. Hash non salé → rainbow tables (leçon 7). Clé courte ou maison → force brute, analyse de fréquence. Nonce réutilisé → la confidentialité tombe. C'est presque toujours la mauvaise utilisation qu'on casse, pas l'algorithme lui-même.

L'arsenal.

  • CyberChef : le couteau suisse. Décode base64/hexa/URL, casse ROT/XOR, identifie un hash, le tout en quelques clics. À avoir sous la main.
  • hashcat / John the Ripper : casser les hash (leçon 7).
  • testssl.sh / SSL Labs : auditer la config TLS d'un serveur (vieux protocoles, ciphers faibles, certificat).

Les pièges qui font fuiter

On ne reviendra pas sur la confusion encoder/chiffrer (vue plus haut). Trois autres erreurs reviennent sans cesse :

1. Chiffrer les mots de passe au lieu de les hacher. Le chiffrement est réversible : si la clé fuite, tous les mots de passe fuitent d'un coup. On les hache (bcrypt/Argon2, leçon 7), un sens unique d'où l'on ne revient pas.

2. Inventer son algorithme. « Personne ne connaît mon chiffrement » est l'erreur historique : la sécurité par l'obscurité ne tient pas. La solidité vient de la clé, pas du secret du code.

3. Laisser un secret dans le code. Une clé en dur reste dans l'historique git ; une fois commitée, la retirer ne suffit pas, il faut la changer (rotation).

Trois pièges, un seul remède : la checklist ci-dessous.

La checklist crypto

À se poser dès qu'on touche à une donnée sensible.

  • Le bon outil : encoder pour transporter, hacher pour vérifier/stocker un mot de passe, chiffrer pour un secret relisible.
  • Mots de passe : bcrypt ou Argon2 (leçon 7), jamais chiffrés, jamais en md5/sha1.
  • Chiffrement : libsodium ou AES-GCM (authentifié), jamais ECB, nonce unique à chaque fois.
  • Transport : HTTPS/TLS partout, certificat Let's Encrypt, HSTS.
  • Secrets : hors du code (.env non versionné, gestionnaire de secrets), rotation, jamais commités.
  • Règle d'or : jamais son propre algorithme de chiffrement.

Les références. La OWASP Cryptographic Storage Cheat Sheet donne les bons choix par cas d'usage. Pour coder : la doc de libsodium (et son binding PHP intégré). Pour jouer avec encodages et hash : CyberChef.

Rappel. Casser la « crypto » d'un système qui n'est pas le vôtre reste une attaque. On ne teste que ses propres systèmes ou une cible explicitement autorisée (leçon 1).

"I encoded it, so it's protected"

Lesson 11 showed HSTS forcing HTTPS across the whole connection. But that HTTPS is only as good as the crypto underneath it — not a base64 mistaken for encryption. That's exactly the trap this dev falls into:

They want to protect the API keys in their config file. They run them through base64, see an unreadable blob, and figure it's secure:

API_KEY=c2stbGl2ZS04YTdkM2UtcHJvZA==

An attacker who reads the file types one command, just one:

echo 'c2stbGl2ZS04YTdkM2UtcHJvZA==' | base64 -d
sk-live-8a7d3e-prod

In one second, the key is in clear. base64 is not encryption: it's just another way to write the same bytes. Anyone can read it back, with no key. This confusion between encoding, hashing and encrypting is behind countless leaks. It maps to OWASP 2025 (A04 Cryptographic Failures).

Encode, hash, encrypt: never confuse them

Three operations, three different goals. Mixing them up creates the flaw.

  • Encode (base64, URL-encoding): change a data's shape to transport it. Reversible by anyone, no key. It protects nothing. It's just a different alphabet.
  • Hash (SHA-256, bcrypt): turn data into a fingerprint, one-way. You never go back. Used to verify integrity and to store passwords (lesson 7).
  • Encrypt (AES, RSA): make data unreadable, reversible with a key. Used to protect a secret you'll need to read back. Without the key, unreadable.

The rule fits in one line: encode to transport, hash to verify or store a password, encrypt to protect a secret you'll read back. The dev's base64 from the start was neither hashing nor encryption: just a disguise, removed with one command.

Encode, hash, encrypt: three operations, three goals Encode (base64): reversible by anyone, no key Hash (bcrypt): one-way, irreversible Encrypt (AES): reversible, but only with the key
Encoding protects nothing (gray). Hashing and encryption protect (green), but differently: one is one-way, the other decrypts with the key.

Beware the false cure: "I base64-encoded it" or "I wrote my own little algorithm" protects nothing. Encoding gets decoded, and a homemade algorithm breaks in minutes. Security doesn't come from the secrecy of the method, but from real crypto with a real key (section 4).

Your turn to attack: break the fake protection

You've stolen a "protected" secret. Pick the protection method, then try to break it without the key. You'll watch base64 and homemade crypto fall in one click, and AES-256 hold. Everything is computed in your browser.

Stolen secret · "protected" form

        
        

    
What just happened

base64 decodes with a standard function (atob, base64 -d): no key, no protection. Homemade crypto (short-key XOR) breaks almost as fast: the key is guessed or brute-forced, and the pattern shows. Both were just obscurity.

AES-256 holds: without the key, you'd have to try 2256 possibilities, more than there are atoms on Earth. That's real encryption: security rests on the key, not on the secrecy of the algorithm (which is public and studied by the whole world).

The fix: real crypto, and the golden rule

First, two families of encryption. Symmetric (AES) uses one shared key, fast. Asymmetric (RSA, elliptic curves) uses a pair: a public key to encrypt, a private key to decrypt. It solves key sharing. In practice, HTTPS combines both: asymmetric exchanges a symmetric key, then symmetric does the heavy lifting, faster.

Symmetric encryption: one shared key. Asymmetric: a public key to encrypt, a private key to decrypt. Symmetric (AES): the same key encrypts and decrypts Message Encrypted Message key K key K Asymmetric (RSA): two different keys, a pair Message Encrypted Message public key private key
Symmetric: one secret key, fast, but you must manage to share it. Asymmetric: you encrypt with the public key and decrypt with the private key, which solves the sharing.

On the code side, you don't write the crypto yourself: you call a vetted library. In PHP, libsodium is built in and does authenticated encryption (encrypted + protected against tampering):

$key    = sodium_crypto_secretbox_keygen();                        // random key (kept separately)
$nonce  = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);        // a unique nonce each time
$cipher = sodium_crypto_secretbox($message, $nonce, $key);         // encrypted + authenticated
$plain  = sodium_crypto_secretbox_open($cipher, $nonce, $key);     // decrypted

Two more essential reflexes. HTTPS/TLS encrypts everything in transit (cookies, passwords): without it, everything travels in clear on the network (lesson 8). The certificate is free (Let's Encrypt), and you add HSTS (lesson 11). And secrets (keys, database passwords) never live hardcoded in the code: they'd end up in the git history. Put them elsewhere: environment variables, an unversioned .env file outside the web root, or a secret manager.

The golden rule: never roll your own crypto. A good algorithm is public and holds anyway (Kerckhoffs's principle): its strength comes from the key, not the secrecy of the code. The standard algorithms have already survived years of attacks. Inventing your own starts from scratch, without that trial by fire. Use AES-GCM or libsodium, never ECB mode (which leaks patterns), and never reuse a nonce.

Defense in depth:

  1. the right tool: encode to transport, hash for passwords (bcrypt/Argon2, lesson 7), encrypt for a readable-back secret;
  2. a vetted library (libsodium), authenticated encryption, never ECB, a unique nonce;
  3. HTTPS everywhere (+ HSTS), a Let's Encrypt certificate;
  4. secrets out of the code: unversioned .env, a secret manager, rotation.

Reference: OWASP Cryptographic Storage Cheat Sheet.

The pentester's method and arsenal

You now know how to defend. Here's exactly how an attacker reads what you thought you'd hidden.

1. Spot the fake protection. A string ending in == smells of base64; eyJ... is a JWT (JSON Web Token, an authentication token base64-encoded — so readable with no key). A "encrypted" cookie that decodes on its own, a "secret" token that's only encoding: you see it fast.

2. Identify the algorithm. Length gives away the hash: 32 hex characters = md5, 40 = sha1, 64 = sha256. The format (hex, base64) and mode give the rest.

3. Break the weak one. Unsalted hash → rainbow tables (lesson 7). Short or homemade key → brute force, frequency analysis. Reused nonce → confidentiality collapses. It's almost always the misuse you break, not the algorithm itself.

The arsenal.

  • CyberChef: the Swiss army knife. Decodes base64/hex/URL, breaks ROT/XOR, identifies a hash, all in a few clicks. Keep it handy.
  • hashcat / John the Ripper: crack hashes (lesson 7).
  • testssl.sh / SSL Labs: audit a server's TLS config (old protocols, weak ciphers, certificate).

The traps that cause leaks

We won't revisit the encode/encrypt confusion (covered above). Three other mistakes come up again and again:

1. Encrypting passwords instead of hashing them. Encryption is reversible: if the key leaks, all passwords leak at once. You hash them (bcrypt/Argon2, lesson 7), a one-way street you can't come back from.

2. Inventing your own algorithm. "No one knows my cipher" is the historic mistake: security through obscurity doesn't hold. Strength comes from the key, not the secrecy of the code.

3. Leaving a secret in the code. A hardcoded key stays in the git history; once committed, removing it isn't enough, you must change it (rotation).

Three traps, one remedy: the checklist below.

The crypto checklist

To ask yourself as soon as you touch sensitive data.

  • The right tool: encode to transport, hash to verify/store a password, encrypt for a readable-back secret.
  • Passwords: bcrypt or Argon2 (lesson 7), never encrypted, never md5/sha1.
  • Encryption: libsodium or AES-GCM (authenticated), never ECB, a unique nonce each time.
  • Transport: HTTPS/TLS everywhere, a Let's Encrypt certificate, HSTS.
  • Secrets: out of the code (unversioned .env, secret manager), rotation, never committed.
  • Golden rule: never your own crypto.

The references. The OWASP Cryptographic Storage Cheat Sheet gives the right choices per use case. For coding: the libsodium docs (and its built-in PHP binding). To play with encodings and hashes: CyberChef.

Reminder. Breaking the "crypto" of a system that isn't yours is still an attack. Only test your own systems or an explicitly authorized target (lesson 1).

Prédisez avant de lire

Un dev « sécurise » ses clés d'API en les passant en base64 dans le fichier de config. Un attaquant vole le fichier. Avant de dérouler : en combien de temps récupère-t-il les clés ? Et si elles étaient chiffrées en AES-256 avec la clé gardée ailleurs ?

Voir la réponse

En une seconde avec base64 : c'est un encodage, pas un chiffrement. base64 -d (ou n'importe quel outil en ligne) rend les clés en clair, sans aucune clé secrète. Avec AES-256 et la clé gardée ailleurs (hors du fichier volé), l'attaquant n'a que du charabia : sans la clé, il faudrait essayer 2256 possibilités, ce qui est hors d'atteinte. Le base64 ne protège rien ; le vrai chiffrement protège tant que la clé reste secrète.

Predict before reading on

A dev "secures" their API keys by base64-encoding them in the config file. An attacker steals the file. Before you expand: how long to recover the keys? And if they were AES-256 encrypted with the key kept elsewhere?

Show the answer

In one second with base64: it's encoding, not encryption. base64 -d (or any online tool) returns the keys in clear, with no secret key. With AES-256 and the key kept elsewhere (outside the stolen file), the attacker only has gibberish: without the key, they'd have to try 2256 possibilities, which is out of reach. base64 protects nothing; real encryption protects as long as the key stays secret.

🎯 Pratique

S'entraîner (clique pour ouvrir) :

💬 Ré-explique sans regarder
Ré-explique sans regarder

Avec tes mots : quelle est la différence entre encoder, hacher et chiffrer (et à quoi sert chacun), et pourquoi ne faut-il jamais écrire sa propre crypto ?

Une bonne explication dit : encoder (base64) change la forme d'une donnée pour la transporter, c'est réversible par tous, sans clé : ça ne protège rien. Hacher (bcrypt, SHA-256) est à sens unique : on ne revient pas en arrière ; ça sert à vérifier l'intégrité et à stocker les mots de passe. Chiffrer (AES) rend la donnée illisible mais réversible avec une clé ; ça sert à protéger un secret qu'on devra relire. On n'invente pas son propre algorithme de chiffrement parce qu'un bon algorithme est public et résiste quand même : sa solidité vient de la clé, pas du secret du code. Un algo maison n'a pas encaissé les années d'attaques des standards (AES, libsodium), donc il se casse vite.
🧠 Rappel libre
Rappel libre

Sans remonter : définis en une phrase chacune encoder, hacher, chiffrer (avec un usage), et donne la règle d'or de la crypto.

Encoder : changer la forme d'une donnée (base64) pour la transporter, réversible par tous sans clé ; ne protège rien. Hacher : transformer en empreinte à sens unique (bcrypt, SHA-256) ; sert à vérifier l'intégrité et stocker les mots de passe. Chiffrer : rendre illisible mais réversible avec une clé (AES) ; sert à protéger un secret qu'on relira. Règle d'or : ne jamais inventer son propre algorithme de chiffrement, utiliser une bibliothèque éprouvée (libsodium, AES-GCM) ; la solidité vient de la clé et d'algos publics, pas du secret d'une méthode maison.
⚖️ Juge le code de l'IA
Accepter ou rejeter le code de l'IA

Tu dois protéger des données sensibles en base (des numéros de téléphone). L'IA propose : « Je les encode en base64 avant de les stocker. Comme ça ils ne sont pas en clair dans la base, c'est protégé. » Tu acceptes, ou tu rejettes ?

À rejeter, c'est la confusion type. Le base64 n'est pas du chiffrement : c'est un encodage réversible par n'importe qui, sans clé. Les numéros sont donc en clair pour quiconque accède à la base (et l'attaquant qui vole la base les décode en une commande). Pour des données sensibles qu'on doit relire, il faut un vrai chiffrement : une bibliothèque éprouvée (libsodium, sodium_crypto_secretbox), du chiffrement authentifié, et surtout la clé gardée hors de la base (sinon voler la base = voler la clé). Idéalement, on se demande aussi si on a vraiment besoin de stocker cette donnée. Le base64, lui, ne fait que déguiser.
Un secret est « protégé » en base64 dans un fichier volé. Quel est son niveau de protection ?
Pour stocker des mots de passe, faut-il les chiffrer ou les hacher ?
Quelle est la règle d'or de la cryptographie appliquée ?
Laquelle de ces opérations est réversible AVEC une clé (mais illisible sans) ?
Next step

You protect your secrets and your data. But your code rests on hundreds of dependencies you didn't write. Lesson 13 attacks from there: a flaw in a dependency is your flaw, and how to audit it.

Lesson 13: Dependencies & supply chain →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement