Chapitre 1.3 Quiz - Cryptographie et securite reseau
Mode Quiz - Toutes les reponses sont masquees. Tentez chaque question avant de reveler la reponse.
Question 1
Un developpeur stocke des cles API en les hachant avec SHA-256 avant de les stocker en base de donnees. Il affirme que c'est aussi securise que bcrypt car "SHA-256 est un algorithme fort." Expliquez precisement pourquoi SHA-256 est le mauvais choix pour ce cas d'usage, et quelle est l'approche correcte. Incluez des taux d'attaque specifiques.
Reveler la reponse et l'explication
Reponse : SHA-256 est incorrect pour le stockage d'identifiants car il est concu pour etre rapide - permettant la force brute a des milliards de hachages par seconde. bcrypt ou Argon2 sont corrects car ils sont deliberement lents.
Explication :
Comparaison de vitesse - taux de force brute GPU (RTX 4090) :
| Algorithme | Hachages/seconde | Temps pour cracker un hachage de 8 caracteres minuscules |
|---|---|---|
| MD5 | ~164 milliards/sec | < 1 seconde |
| SHA-256 | ~23 milliards/sec | ~2 secondes |
| bcrypt (cout 12) | ~184 000/sec | ~48 ans |
| Argon2id | ~1 000/sec | Astronomique |
SHA-256 a ete concu pour la vitesse - il est utilise pour hacher des gigaoctets de donnees rapidement pour la verification d'integrite. 23 milliards de tentatives par seconde signifie qu'une cle API de 8 caracteres utilisant uniquement des lettres minuscules (26^8 = 208 milliards de combinaisons) est craquee en moins de 10 secondes sur un seul GPU.
Probleme supplementaire - absence de sel :
Sans sel (octets aleatoires ajoutes a chaque identifiant avant le hachage), des entrees identiques produisent des sorties identiques. Un attaquant peut precomputer des tables arc-en-ciel - des milliards de correspondances hachage->texte clair - et retrouver instantanement n'importe quel identifiant dans la base de donnees.
L'approche correcte :
# Correct : Argon2id avec sel (Argon2 gere le sel en interne)
from argon2 import PasswordHasher
ph = PasswordHasher(
time_cost=3, # 3 iterations
memory_cost=65536, # 64 Mo de RAM par hachage - le GPU a une VRAM limitee par thread
parallelism=2, # 2 threads
hash_len=32,
salt_len=16
)
# Stocker ceci en base de donnees
api_key_hash = ph.hash("sk-prod-xK9mN2pQr7vL")
# Verifier : Argon2 extrait le sel et les parametres de la chaine de hachage automatiquement
is_valid = ph.verify(api_key_hash, "sk-prod-xK9mN2pQr7vL")
Encore mieux pour les cles API : Les hacher avec HMAC-SHA256 en utilisant un secret cote serveur :
import hmac, hashlib, os, secrets
# Generer une cle API
raw_key = secrets.token_urlsafe(32) # Montrer a l'utilisateur une seule fois
# Stocker le HMAC (hachage avec cle) en BDD - necessite la connaissance de SERVER_SECRET pour cracker
server_secret = os.environ['API_KEY_SECRET']
stored_hash = hmac.new(server_secret.encode(), raw_key.encode(), hashlib.sha256).hexdigest()
Cela utilise une cle cote serveur, ce qui signifie qu'un attaquant qui vole la base de donnees ne peut pas faire de force brute sans voler egalement le secret serveur.
Question 2
Vous effectuez un audit TLS et executez nmap --script ssl-enum-ciphers -p 443 target.com. La sortie inclut les suites de chiffrement suivantes avec la note C :
TLS_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_3DES_EDE_CBC_SHA
TLS_ECDHE_RSA_WITH_RC4_128_SHA
Pour chaque suite de chiffrement, identifiez la faiblesse specifique et expliquez l'attaque qu'elle permet.
Reveler la reponse et l'explication
Reponse : Chaque suite a une faiblesse cryptographique distincte.
Explication :
Suite 1 : TLS_RSA_WITH_AES_256_CBC_SHA
Faiblesse : Absence de confidentialite persistante (echange de cles RSA statique) + mode CBC (vulnerabilite oracle de rembourrage)
- Le prefixe
TLS_RSA_signifie que l'echange de cles utilise directement la cle RSA statique du serveur. Si la cle privee RSA du serveur est compromise (violation, injonction judiciaire, sauvegarde fuilee), toutes les sessions passees enregistrees peuvent etre dechiffrees. - Le mode CBC sans chiffrement authentifie est vulnerable a BEAST (TLS 1.0) et aux attaques oracle de rembourrage (variante POODLE). Si l'implementation TLS ne verifie pas le rembourrage en temps constant, un attaquant peut dechiffrer des octets individuels.
Suite 2 : TLS_RSA_WITH_3DES_EDE_CBC_SHA
Faiblesse : 3DES est depreciee (attaque Sweet32) + absence de confidentialite persistante
- 3DES a une taille de bloc de 64 bits. En mode CBC, apres ~2^32 blocs (~32 Go de donnees), des collisions d'anniversaire dans les valeurs de blocs se produisent. L'attaque Sweet32 (CVE-2016-2183) exploite ces collisions pour recuperer du texte clair - pratique contre les sessions de longue duree (HTTPS avec keep-alive, VPN).
- La securite effective de 3DES est seulement de 112 bits en raison des attaques meet-in-the-middle sur la variante a trois cles.
- Le NIST a depreciee 3DES en 2017.
Suite 3 : TLS_ECDHE_RSA_WITH_RC4_128_SHA
Faiblesse : RC4 est casse - plusieurs attaques de recuperation de texte clair
- RC4 a des biais statistiques dans son flux de cles. Des attaques comme RC4 NOMORE (2015) recuperent des cookies HTTP en moins de 75 heures avec ~2^26 sessions.
- Les premiers octets du flux de cles RC4 sont fortement biaises - les attaques a texte clair connu (valeurs de cookies a des decalages connus) permettent la recuperation.
- Remarque : le prefixe
ECDHE_RSA_signifie que cette suite dispose bien de la confidentialite persistante - mais RC4 rend le chiffrement lui-meme peu sur independamment. - La RFC 7465 (2015) interdit RC4 dans TLS.
Suites correctes (TLS 1.2) :
# Seules ces suites de chiffrement doivent rester activees pour TLS 1.2 :
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256
# Exigences : ECDHE (confidentialite persistante) + GCM ou POLY1305 (AEAD, pas d'oracle de rembourrage)
Question 3
Une equipe de securite decouvre que les tokens JWT de son application web sont signes avec "alg": "HS256" et que la cle de signature est le mot de passe de la base de donnees de l'application. Les tokens sont valides avec le code Python suivant :
import jwt
def validate_token(token):
decoded = jwt.decode(token, options={"verify_signature": False})
return decoded
Identifiez toutes les vulnerabilites cryptographiques et d'implementation dans cette configuration.
Reveler la reponse et l'explication
Reponse : Trois vulnerabilites critiques - verification de signature desactivee, cle faible/devinable, et susceptibilite a l'attaque alg:none.
Explication :
Vulnerabilite 1 - Signature non verifiee (verify_signature: False)
C'est le probleme le plus critique. options={"verify_signature": False} indique a la bibliotheque JWT de ne pas verifier la signature. N'importe quel attaquant peut forger n'importe quel JWT avec n'importe quelles affirmations et le serveur l'acceptera.
# L'attaquant forge un token admin - aucune cryptographie necessaire
import base64, json
header = base64.urlsafe_b64encode(json.dumps({"alg":"HS256","typ":"JWT"}).encode()).rstrip(b'=')
payload = base64.urlsafe_b64encode(json.dumps({"sub":"attaquant","role":"admin","uid":1}).encode()).rstrip(b'=')
forged_token = f"{header.decode()}.{payload.decode()}.FAKESIGNATURE"
# Le serveur accepte cela car verify_signature=False
Vulnerabilite 2 - Attaque alg:none
Meme si la verification de signature etait activee, si le serveur accepte n'importe quel algorithme specifie dans l'en-tete JWT, un attaquant peut definir "alg": "none" et omettre completement la signature. Certaines bibliotheques JWT traitaient historiquement alg:none comme valide.
# Forger un token avec alg:none
import base64, json
header = base64.urlsafe_b64encode(json.dumps({"alg":"none","typ":"JWT"}).encode()).rstrip(b'=')
payload = base64.urlsafe_b64encode(json.dumps({"sub":"admin","role":"superuser"}).encode()).rstrip(b'=')
token = f"{header.decode()}.{payload.decode()}." # Pas de signature
Vulnerabilite 3 - Mot de passe de base de donnees comme cle de signature
HS256 est HMAC-SHA256 - la cle de signature doit rester secrete, mais :
- Les mots de passe de base de donnees apparaissent dans les chaines de connexion, fichiers de configuration, journaux, variables d'environnement, sauvegardes
- Si le mot de passe BDD est change (bonne pratique), tous les tokens deviennent invalides
- Les mots de passe choisis par des humains ont une faible entropie - susceptibles a la force brute
Un attaquant disposant d'un JWT valide signe avec une cle faible peut le cracker hors ligne :
# Force brute de la cle de signature JWT HS256 avec hashcat
hashcat -a 0 -m 16500 eyJhbGci...token.jwt rockyou.txt
# jwt_tool pour les attaques JWT
python3 jwt_tool.py eyJhbGci...token.jwt -C -d rockyou.txt # Cracker la cle
python3 jwt_tool.py eyJhbGci...token.jwt -X a # Attaque alg:none
python3 jwt_tool.py eyJhbGci...token.jwt -I -pc role -pv admin # Injection de revendication
Implementation correcte :
import jwt, os
from cryptography.hazmat.primitives.asymmetric import rsa
# Option 1 : HS256 avec une cle aleatoire appropriee
SECRET_KEY = os.environ['JWT_SECRET'] # Doit etre aleatoire, 256 bits, depuis secrets.token_bytes(32)
def validate_token_correct(token):
try:
decoded = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"], # Lister explicitement - n'accepter jamais 'none'
options={"require": ["exp", "iat", "sub"]} # Exiger les revendications standard
)
return decoded
except jwt.ExpiredSignatureError:
raise ValueError("Token expire")
except jwt.InvalidTokenError as e:
raise ValueError(f"Token invalide : {e}")
# Option 2 : RS256 avec paire de cles appropriee (meilleur pour les microservices)
# Signer avec la cle privee, verifier avec la cle publique seulement
Question 4
Expliquez ce que signifie la confidentialite persistante (perfect forward secrecy), pourquoi un serveur utilisant uniquement TLS_RSA_WITH_AES_256_GCM_SHA384 n'a PAS de confidentialite persistante, et demontrez avec un scenario d'attaque pratique ou l'absence de confidentialite persistante est exploitee.
Reveler la reponse et l'explication
Reponse : La confidentialite persistante signifie que les sessions passees restent securisees meme si la cle privee a long terme est compromise ulterieurement. L'echange de cles RSA statique n'en dispose pas car la cle de session est protegee uniquement par la cle privee RSA a long terme du serveur.
Explication :
Ce que signifie la confidentialite persistante :
Dans un protocole avec PFS, chaque session utilise une cle ephemere unique qui est detruite a la fin de la session. La compromission de la cle d'identite a long terme apres coup ne peut pas dechiffrer les sessions passees car la cle de session n'existe plus.
Pourquoi TLS_RSA_WITH_AES_256_GCM_SHA384 manque de PFS :
Dans l'echange de cles RSA statique (le prefixe TLS_RSA_) :
Client Serveur
------ -------
Generer un secret pre-maitre (PMS) aleatoire
Chiffrer PMS avec la cle publique RSA du serveur --------->
Dechiffrer PMS avec la cle privee RSA
Les deux derivent les cles de session depuis PMS
Toute la securite de la session depend du secret de la cle privee RSA statique du serveur. Si un attaquant :
- Enregistre tout le trafic TLS aujourd'hui (collecte passive)
- Compromet la cle privee RSA du serveur dans le futur (violation de donnees, injonction judiciaire, initiee interne)
- Il peut dechiffrer TOUTES les sessions passees enregistrees
Scenario d'attaque pratique - le modele NSA/Etat-nation :
2020 : L'attaquant exploite une fibre, enregistre tout le trafic chiffre vers bank.com
[bank.com utilise TLS_RSA_WITH_AES_256_GCM_SHA384]
L'attaquant stocke : ClientHello, ServerHello, EncryptedPreMasterSecret, tout le texte chiffre
2024 : L'attaquant viole le serveur bank.com (ou achete la cle privee sur le dark web)
Obtient : la cle privee RSA de bank.com
Attaque :
# L'attaquant utilise la cle privee pour dechiffrer les sessions enregistrees de 2020
# Avec Wireshark et la cle privee :
# Edition -> Preferences -> Protocoles -> TLS -> Liste des cles RSA
# Ajouter : IP bank.com, port 443, server.key
# Ou avec ssldump :
ssldump -r capture_2020.pcap -k bank_prive.key -d
# Sortie : toutes les requetes/reponses HTTP de 2020 en texte clair
Avec ECDHE (PFS active) :
Client Serveur
------ -------
Generer une paire de cles ECDH ephemere Generer une paire de cles ECDH ephemere
(a_priv, a_pub) (b_priv, b_pub)
Envoyer a_pub -----------------------> Recevoir a_pub
Recevoir b_pub <----------------------- Envoyer b_pub (signe avec la cle privee RSA)
Calculer : partage = ECDH(a_priv, b_pub) Calculer : partage = ECDH(b_priv, a_pub)
Deriver les cles de session depuis partage Deriver les cles de session depuis partage
[Apres la session : a_priv et b_priv sont supprimes]
Maintenant, si la cle privee RSA est compromise, l'attaquant peut seulement verifier l'identite du serveur - il ne peut pas calculer a_priv ou b_priv (ephemeres, supprimes), donc ne peut pas deriver les cles de session.
Verification :
# Verifier si un serveur utilise l'echange de cles ephemere (PFS)
openssl s_client -connect target.com:443 2>/dev/null | grep "Server Temp Key"
# Avec PFS : "Server Temp Key: X25519, 253 bits"
# Sans PFS : [pas de ligne Server Temp Key - utilise RSA statique]
# Imposer PFS dans la selection de suite de chiffrement (supprimer toutes les suites TLS_RSA_)
# Toute suite commencant par ECDHE_ ou DHE_ dispose de PFS
openssl ciphers -v 'ECDHE+AESGCM:ECDHE+CHACHA20' | column -t