Concevoir l'échec
Authentifier avant de parser, erreur opaque, frontières de confiance : comment la manière dont un système échoue décide de sa sécurité.
Dans le module 1, tu as vu qu’un oracle de padding Oracle de padding Attaque exploitant tout signal (message d'erreur, temps de réponse, comportement observable) qui révèle si le rembourrage d'un bloc chiffré est valide. En itérant les modifications du chiffré et en observant les réponses, un attaquant peut déchiffrer le message sans connaître la clé. C'est l'une des raisons historiques majeures pour lesquelles le chiffrement seul sans authentification est dangereux. permettait à un adversaire de reconstruire un texte clair octet par octet, sans jamais connaître la clé, simplement en observant si le serveur acceptait ou rejetait un message. Ce que l’adversaire exploitait n’était pas l’algorithme. C’était le comportement du système à l’échec : deux réponses distinctes, selon la cause du rejet. Cette distinction observable était l’oracle.
L’AEAD élimine les oracles de padding en retirant à l’adversaire toute information sur le résultat du déchiffrement. Mais un système qui utilise correctement un algorithme AEAD peut quand même recréer un oracle, par sa façon d’échouer, de parser, ou de valider les octets entrants. Les erreurs de conception à ce niveau annulent les garanties cryptographiques.
Ce module examine les règles de conception qui ferment ces failles. C’est le module final du cours : les principes qui suivent s’appuient sur l’intégrité et la confidentialité du module 1, l’unicité du nonce du module 2, et la liaison au contexte du module 3.
À la fin de ce module, tu sauras expliquer pourquoi l’ordre d’opérations (vérifier avant de parser) est une propriété de sécurité, définir ce qu’est une erreur opaque et pourquoi elle est nécessaire, appliquer le principe de frontière de confiance à un flux de traitement, et identifier les cas où un système bien chiffré reste vulnérable par sa logique d’échec.
Authentifier avant de parser
La règle est absolue : vérifier le tag avant de toucher un seul octet du texte chiffré.
L’ordre paraît évident une fois énoncé, mais les violations sont courantes. Un système qui parse les octets chiffrés avant d’avoir vérifié leur authenticité expose sa logique de parsing à des données entièrement contrôlées par l’adversaire. Chaque branchement conditionnel dans ce parseur devient une fuite potentielle : le comportement du système (temps de réponse, message d’erreur, code de retour) peut varier selon la structure des octets, et cette variation est observable.
Prenons un exemple. Un service reçoit une requête API chiffrée dont le premier champ est une longueur de payload. Si le service lit ce champ et alloue un buffer de la taille annoncée avant de vérifier le tag, un adversaire peut envoyer une valeur de longueur arbitraire et observer si l’allocation réussit ou échoue. Le parseur a traité des données non authentifiées. L’adversaire a obtenu une information.
Le même raisonnement s’applique à toute logique conditionnelle exécutée avant la vérification : décodage d’un format, lecture d’un champ de version, validation d’un identifiant. Si ces opérations s’effectuent sur des octets non authentifiés, elles participent à l’oracle.
La vérification du tag est une précondition, pas une étape parmi d’autres dans le traitement.
Un schéma AEAD correct applique déjà ce principe dans son opération open : le tag est vérifié en premier, et si la vérification échoue, aucun octet de texte clair n’est retourné. La règle s’étend à la couche applicative : même après l’open réussi, le texte clair obtenu doit être traité comme une donnée de source externe jusqu’à ce que son contenu soit validé.
Parse, don’t validate
Une fois le tag vérifié, les octets du texte clair sont authentiques, mais ils peuvent encore être malformés. La règle “parse, don’t validate” dit : transformer les octets bruts en valeurs bien typées à la frontière d’entrée, une seule fois, de sorte que le reste du code ne puisse pas recevoir de données malformées.
La distinction est précise. Valider, c’est vérifier qu’une donnée satisfait une condition (par exemple : “ce champ est un entier positif”) et continuer à manipuler la donnée brute si la condition est vraie. Parser, c’est construire une représentation typée depuis les octets bruts, et retourner une erreur si la construction échoue.
L’avantage du parsing est structurel : une fois la frontière franchie, les types garantissent la cohérence des données. Un champ “longueur” qui n’existe qu’en tant qu’entier non signé borné ne peut pas être négatif ailleurs dans le code, par construction du type, pas par vérification répétée.
Ce principe s’applique à toute couche qui reçoit des octets de l’extérieur : déchiffrement de tokens, lecture de paquets réseau, décodage de messages. La frontière de parsing coïncide avec la frontière de confiance. Ce qui passe cette frontière est utilisable. Ce qui échoue à la passer est rejeté, sans traitement partiel.
L’erreur opaque
Un système qui échoue de plusieurs façons observables est un oracle. La conséquence de conception est directe : un seul mode d’échec, sans information exploitable.
Ce que cela signifie concrètement :
- Un seul message d’erreur. “Déchiffrement échoué” couvre tous les cas : tag invalide, nonce mal formé, AAD absente, données corrompues. Distinguer ces cas dans le message retourné permet à l’adversaire de savoir ce qui a échoué et pourquoi, ce qui suffit à reconstruire des informations sur le message original.
- Un seul code de retour. Même logique pour les codes d’erreur HTTP, les codes de statut applicatifs, les exceptions levées. Toute distinction observable entre les causes d’échec est une fuite.
- Un temps de réponse constant. Comme vu dans le module 2 avec la comparaison de tags en temps constant Temps constant Propriété d'une implémentation dont la durée d'exécution ne dépend pas des valeurs secrètes traitées, éliminant les canaux auxiliaires temporels. Elle est indispensable pour la comparaison de tags d'authentification et pour les opérations cryptographiques sensibles. ChaCha20-Poly1305 est naturellement en temps constant en logiciel, tandis qu'AES nécessite des instructions matérielles (AES-NI) pour atteindre cette propriété. , une différence de temps de traitement entre deux causes d’échec est un canal caché. Un rejet qui arrive en 2 millisecondes et un rejet qui arrive en 20 millisecondes sont deux messages distincts pour un adversaire qui peut mesurer.
L’erreur opaque s’applique aussi aux journaux internes : un log détaillé accessible via une interface externe (un endpoint de métriques, une page de diagnostic) peut devenir une fuite si l’adversaire peut le consulter. Les journaux internes peuvent être précis ; ce qui est retourné au client ne doit pas l’être.
Les frontières de confiance
Une frontière de confiance est le point où des octets non contrôlés entrent dans le système. Tout ce qui vient de l’extérieur de cette frontière est hostile par défaut.
Dans un système qui utilise l’AEAD, la frontière de confiance est l’opération open : ce qui arrive avant l’open est non authentifié, ce qui sort de l’open réussi est authentifié. Mais cette frontière a des implications pratiques sur la conception du système.
Valider dans les deux sens. Le seal doit vérifier que les champs inclus dans l’AAD sont bien formés, pas seulement les passer tels quels. Si l’émetteur inclut dans l’AAD un identifiant de destinataire sans valider qu’il correspond à un destinataire existant, le chiffré peut être forgé avec un identifiant invalide et le destinataire ne détectera l’erreur qu’après le déchiffrement. La validation des champs de contexte au moment du seal réduit la surface exploitable dès l’émission.
Traiter tout ce qui vient de l’extérieur comme hostile. Cela inclut les octets chiffrés (évident), mais aussi les métadonnées qui accompagnent le chiffré : un en-tête HTTP, un identifiant de session transmis séparément, un paramètre d’URL. Ces valeurs externes doivent être validées indépendamment, avant d’être utilisées pour sélectionner la clé, l’algorithme ou le destinataire.
La couche cryptographique n’est pas la seule ligne de défense. L’AEAD garantit qu’un message authentifié n’a pas été altéré. Elle ne garantit pas que le message a été construit correctement par un émetteur légitime. Un adversaire interne (un composant compromis, un émetteur qui forge un contexte valide mais malveillant) peut produire un chiffré authentique avec un contenu malveillant. Les validations applicatives après l’open restent nécessaires.
Ordre correct contre ordre incorrect
Le diagramme illustre la différence structurelle. Dans l’ordre correct, l’adversaire ne peut pas influencer le comportement du parseur, car le parseur ne reçoit que des données dont l’authenticité a déjà été établie. Dans l’ordre incorrect, chaque branchement du parseur est potentiellement observable par l’adversaire, qui contrôle entièrement les octets présentés.
Le pattern à retenir
Ne touche aucun octet que tu n’as pas authentifié. Un seul mode d’échec, sans détail exploitable.
Ces deux règles se renforcent mutuellement. La première ferme l’oracle que le parseur pourrait créer. La seconde ferme l’oracle que le mécanisme d’erreur pourrait créer. Ensemble, elles font en sorte que le comportement observable du système à l’échec soit constant : un rejet, une seule façon, sans information sur la cause.
Quiz
1. Un service reçoit un message chiffré avec AES-GCM. Avant de vérifier le tag, il lit le premier champ du chiffré pour allouer un buffer. Pourquoi est-ce une faille ?
2. Dans le module 2, tu as vu que la comparaison de tags doit s'effectuer en temps constant. Quelle propriété générale ce principe illustre-t-il pour la conception des échecs ?
3. Un système scelle un token avec AES-GCM en incluant dans l'AAD l'identifiant de l'utilisateur et la direction du message (module 3). Lors du open, il retourne des messages d'erreur distincts selon que le tag est invalide ou que l'AAD ne correspond pas. Quel est le problème ?
4. En réunissant les quatre modules du cours : un système utilise ChaCha20-Poly1305, génère des nonces aléatoires de 192 bits, inclut l'identifiant de domaine et le destinataire dans l'AAD, vérifie le tag avant de parser, et retourne une erreur unique en cas d'échec. Quelle propriété reste à vérifier pour une mise en oeuvre complète ?
Ce qu’il faut retenir
- Un système peut utiliser correctement un schéma AEAD et quand même créer un oracle Oracle de padding Attaque exploitant tout signal (message d'erreur, temps de réponse, comportement observable) qui révèle si le rembourrage d'un bloc chiffré est valide. En itérant les modifications du chiffré et en observant les réponses, un attaquant peut déchiffrer le message sans connaître la clé. C'est l'une des raisons historiques majeures pour lesquelles le chiffrement seul sans authentification est dangereux. par sa logique d’échec. Ce que l’adversaire observe à l’échec est une surface d’attaque aussi réelle que l’algorithme lui-même.
- Vérifier le tag avant de toucher un seul octet : la vérification est une précondition, pas une étape parmi d’autres. Tout parseur qui s’exécute sur des octets non authentifiés est exposé.
- Parse, don’t validate : transformer les octets bruts en valeurs bien typées à la frontière d’entrée, une seule fois. Ce qui passe la frontière est utilisable ; ce qui échoue est rejeté sans traitement partiel.
- Un seul mode d’échec, sans information exploitable : même message d’erreur, même code de retour, même temps de traitement, quelle que soit la cause du rejet. Une différence observable est un oracle.
- La comparaison de tags en temps constant Temps constant Propriété d'une implémentation dont la durée d'exécution ne dépend pas des valeurs secrètes traitées, éliminant les canaux auxiliaires temporels. Elle est indispensable pour la comparaison de tags d'authentification et pour les opérations cryptographiques sensibles. ChaCha20-Poly1305 est naturellement en temps constant en logiciel, tandis qu'AES nécessite des instructions matérielles (AES-NI) pour atteindre cette propriété. (module 2) est une instance de cette règle générale : le temps de réponse est une information observable.
- Tout ce qui vient de l’extérieur est hostile par défaut : valider les champs de contexte au seal et au open, ne pas supposer que la couche cryptographique remplace les validations applicatives.
- Les quatre modules forment un tout : l’AEAD garantit l’intégrité et la confidentialité (module 1) à condition que le nonce soit unique (module 2), que le contexte soit lié au chiffré (module 3), et que le système échoue sans donner prise à un oracle (module 4). Chaque couche est nécessaire. Aucune ne remplace les autres.