DJB et la sécurité par construction : de qmail à Curve25519
Un regard pratique sur les idées de sécurité par construction de Daniel J. Bernstein — de qmail à Curve25519 — et ce que signifie en pratique une « crypto simple et vérifiable ».

Ce que signifie la sécurité par construction (sans le jargon)
La sécurité par construction consiste à construire un système de sorte que les erreurs courantes soient difficiles à commettre — et que les dégâts des erreurs inévitables soient limités. Plutôt que de dépendre d’une longue liste de vérifications (« pensez à valider X, nettoyer Y, configurer Z… »), vous concevez le logiciel de sorte que le chemin le plus sûr soit aussi le plus simple.
Pensez-y comme un emballage « enfant sûr » : il ne suppose pas que tout le monde sera parfaitement précautionneux ; il suppose que les humains sont fatigués, occupés et parfois enclins à l’erreur. Un bon design réduit la part de « comportement parfait » demandée aux développeurs, aux opérateurs et aux utilisateurs.
Pourquoi la simplicité réduit le risque
Les problèmes de sécurité se cachent souvent dans la complexité : trop de fonctionnalités, trop d’options, trop d’interactions entre composants. Chaque bouton supplémentaire peut créer un nouveau mode de défaillance — une façon inattendue pour le système de se briser ou d’être détourné.
La simplicité aide de deux façons pratiques :
- Moins de code à auditer : moins de branches, moins de cas particuliers, moins de comportements cachés.
- Moins de façons de mal configurer : lorsqu’il y a un seul choix sûr plutôt que dix « flexibles », il y a moins de place pour l’insécurité accidentelle.
Il ne s’agit pas de minimalisme pour le plaisir. Il s’agit de garder l’ensemble des comportements suffisamment petit pour qu’on puisse réellement le comprendre, le tester et raisonner sur ce qui se passe quand quelque chose tourne mal.
Ce que couvre ce billet (et ce qu’il ne couvre pas)
Cet article utilise le travail de Daniel J. Bernstein comme exemples concrets de sécurité par construction : comment qmail visait à réduire les modes de défaillance, comment la pensée « temps constant » évite des fuites invisibles, et comment Curve25519/X25519 et NaCl poussent vers une cryptographie moins sujette aux erreurs d’usage.
Ce qu’il ne fera pas : fournir une histoire complète de la cryptographie, prouver mathématiquement la sécurité d’algorithmes, ou prétendre qu’il existe une unique « meilleure » bibliothèque pour chaque produit. Il ne prétendra pas non plus que de bons primitives résolvent tout — les systèmes réels échouent encore à cause de la gestion des clés, d’erreurs d’intégration et de lacunes opérationnelles.
L’objectif est simple : montrer des motifs de conception qui rendent les résultats sécurisés plus probables, même quand vous n’êtes pas un spécialiste en cryptographie.
Qui est Daniel J. Bernstein et pourquoi on cite son travail
Daniel J. Bernstein (souvent « DJB ») est un mathématicien et informaticien dont les travaux reviennent régulièrement dans l’ingénierie de sécurité pratique : systèmes de mail (qmail), primitives et protocoles cryptographiques (notamment Curve25519/X25519), et bibliothèques qui empaquettent la crypto pour un usage réel (NaCl).
On cite DJB non pas parce qu’il aurait écrit la seule « bonne » façon de faire de la sécurité, mais parce que ses projets partagent un ensemble cohérent d’instincts d’ingénierie qui réduisent le nombre de façons dont les choses peuvent mal tourner.
Ce que les ingénieurs empruntent au style DJB
Un thème récurrent est des interfaces plus petites et plus resserrées. Si un système expose moins de points d’entrée et moins d’options de configuration, il est plus facile à relire, plus facile à tester et plus difficile à utiliser par erreur.
Un autre thème est des hypothèses explicites. Les défaillances de sécurité surviennent souvent à partir d’attentes non dites — sur le hasard, le comportement temporel, le traitement des erreurs ou le stockage des clés. Les écrits et implémentations de DJB tendent à rendre le modèle de menace concret : qu’est-ce qui est protégé, contre qui et dans quelles conditions.
Enfin, il y a une préférence pour des paramètres par défaut sûrs et la correction ennuyeuse. Beaucoup de conceptions dans cette tradition cherchent à éliminer les arêtes vives qui mènent à des bugs subtils : paramètres ambigus, modes optionnels, et raccourcis de performance qui fuient de l’information.
Pas une biographie — une perspective d’ingénierie
Cet article n’est pas une histoire de vie ni un débat sur des personnalités. C’est une lecture d’ingénierie : quels motifs on observe dans qmail, la pensée temps-constant, Curve25519/X25519 et NaCl, et comment ces motifs se traduisent en systèmes plus simples à vérifier et moins fragiles en production.
qmail : un exemple pratique de conception pour réduire les modes de défaillance
qmail a été construit pour résoudre un problème peu glamour : livrer les emails de façon fiable tout en traitant le serveur de mail comme une cible à haute valeur. Les systèmes de mail sont exposés sur Internet, reçoivent des entrées hostiles toute la journée et manipulent des données sensibles (messages, identifiants, règles de routage). Historiquement, un bug dans un démon de mail monolithique pouvait signifier une compromission totale du système — ou une perte silencieuse de messages que personne ne remarque avant qu’il ne soit trop tard.
Diviser le travail, réduire le rayon d’explosion
Une idée clé dans qmail est de découper la « livraison du mail » en petits programmes qui font chacun une seule tâche : réception, mise en file, livraison locale, livraison distante, etc. Chaque pièce a une interface étroite et des responsabilités limitées.
Cette séparation importe parce que les défaillances deviennent locales :
- Si un composant plante, il ne corrompt pas automatiquement la file ni ne fait tomber tout le système.
- Si un composant a une vulnérabilité, l’attaquant n’obtient pas instantanément les privilèges de toutes les autres parties.
- Si vous pouvez raisonner sur un composant isolément, vous pouvez le tester et l’auditer plus efficacement.
C’est la sécurité par construction en forme pratique : concevoir le système de sorte qu’« une erreur » ait moins de chances de devenir une « défaillance totale ».
Habitudes de conception à copier
qmail modèle aussi des habitudes qui se traduisent bien au-delà de la messagerie :
- Frontières claires : définir exactement quelles entrées un composant accepte et quelles sorties il produit. De petits contrats explicites sont plus faciles à appliquer.
- Traitement strict des entrées : considérer tout ce qui vient du réseau comme potentiellement malveillant ; valider tôt, rejeter les cas étranges et éviter les conjectures « utiles ».
- Principe du moindre privilège par défaut : exécuter les composants avec seulement les permissions dont ils ont besoin, pour qu’un bug n’entraîne pas une prise de contrôle totale.
La conclusion n’est pas « utilisez qmail ». C’est que vous pouvez souvent obtenir de grands gains de sécurité en repensant l’architecture autour de moins de modes de défaillance — avant d’écrire plus de code ou d’ajouter plus de boutons.
Réduire la surface d’attaque par des interfaces serrées
La « surface d’attaque » est la somme de tous les endroits où votre système peut être pincé, sondé ou trompé pour faire la mauvaise chose. Une analogie utile est une maison : chaque porte, fenêtre, ouvre-porte de garage, clé de rechange et fente de livraison est un point d’entrée potentiel. Vous pouvez mettre de meilleures serrures, mais vous gagnez aussi en sécurité en ayant moins de points d’entrée.
Le logiciel est identique. Chaque port ouvert, format de fichier accepté, point d’administration exposé, bouton de configuration ajouté et crochet de plugin supporté augmente le nombre de façons dont les choses peuvent échouer.
Interfaces serrées : APIs plus petites, moins de modes de défaillance
Une « interface serrée » est une API qui fait moins, accepte moins de variations et refuse les entrées ambiguës. Cela semble souvent contraignant — mais c’est plus facile à sécuriser parce qu’il y a moins de chemins de code à auditer et moins d’interactions surprenantes.
Considérez deux conceptions :
- Interface large : « Téléversez n’importe quel type de fichier ; on détectera le format ; compression optionnelle ; chiffrement optionnel ; métadonnées optionnelles ; plusieurs schémas d’auth. »
- Interface serrée : « Téléversez des octets ; vous devez déclarer le type de contenu à partir d’une petite liste blanche ; taille max fixe ; le chiffrement est géré en interne ; une méthode d’authentification. »
La seconde conception réduit ce que les attaquants peuvent manipuler. Elle réduit aussi ce que votre équipe peut mal configurer par accident.
Pourquoi moins d’options peut être plus sûr
Les options multiplient les tests. Si vous supportez 10 bascules, vous n’avez pas 10 comportements — vous avez des combinaisons. Beaucoup de bugs de sécurité se cachent dans ces plis : « ce flag désactive un contrôle », « ce mode saute la validation », « ce réglage legacy contourne les limites de débit ». Les interfaces serrées transforment la « sécurité choisissez votre aventure » en un chemin bien éclairé.
Liste de contrôle : où la complexité se cache
Utilisez ceci pour repérer la surface d’attaque qui grandit discrètement :
- Multiples types d’entrée : plusieurs formats de fichiers, encodages ou « détection automatique ».
- Trop de façons d’entrer : ports réseau supplémentaires, panneaux d’administration, endpoints de debug, webhooks.
- Flags fonctionnels changeant la logique de sécurité : bascules qui modifient la validation, l’auth ou le comportement crypto.
- Extensibilité : scripts, templates, plugins ou « expressions personnalisées » évalués à l’exécution.
- Modes de compatibilité ascendante : protocoles anciens, chiffrements dépréciés, versions API obsolètes.
- Valeurs par défaut implicites : comportement qui change selon les variables d’environnement ou la config manquante.
Quand vous ne pouvez pas réduire l’interface, rendez-la stricte : validez tôt, rejetez les champs inconnus et placez les « fonctionnalités puissantes » derrière des endpoints séparés et clairement limités.
Pensée temps-constant : prévenir des fuites qu’on ne voit pas
Le comportement « temps constant » signifie qu’un calcul prend (à peu près) le même temps quel que soit la valeur d’un secret comme une clé privée, un nonce ou des bits intermédiaires. Le but n’est pas d’être rapide ; c’est d’être ennuyeux : si un attaquant ne peut pas corréler le temps d’exécution avec des secrets, il a beaucoup plus de mal à extraire ces secrets en observant.
Les fuites temporelles comptent parce que les attaquants n’ont pas toujours besoin de casser les mathématiques. S’ils peuvent lancer la même opération plusieurs fois (ou l’observer sur du matériel partagé), de petites différences — microsecondes, nanosecondes, voire effets de cache — peuvent révéler des motifs qui s’accumulent en récupération de clé.
Où la variabilité temporelle se glisse
Même du code « normal » peut se comporter différemment selon les données :
- Branches sur des données secrètes :
if (secret_bit) { ... }change le flux de contrôle et souvent le temps d’exécution. - Accès en table indexés par des secrets : exemple classique où des indices dépendants des secrets touchent différentes lignes de cache.
- Effets de cache et mémoire : les motifs d’accès mémoire dépendant des secrets peuvent fuir via les caches CPU, fautes de page ou préfetch.
- Instructions à durée variable : certaines opérations sur grands entiers, les divisions ou les boucles à sortie précoce peuvent prendre plus de temps pour certains inputs.
Façons haut-niveau d’auditer le risque de timing
Vous n’avez pas besoin de lire de l’assembleur pour tirer de la valeur d’un audit :
- Tracer l’influence des secrets : listez quelles variables sont secrètes (clés privées, secrets partagés, tags d’authentification) et où elles se propagent.
- Chercher les signaux d’alarme : branches dépendantes de secrets, indices de tableaux, boucles avec terminaison basée sur secrets, logique « fast path/slow path ».
- Traiter les dépendances comme partie du modèle de menace : vérifiez que les bibliothèques crypto revendiquent explicitement un comportement en temps constant pour les opérations pertinentes.
- Tester la variance : exécutez les opérations de nombreuses fois avec des secrets différents et mesurez les distributions ; de grandes différences cohérentes sont un signe d’alerte.
La pensée temps-constant est moins une question d’héroïsme qu’une discipline : concevoir le code pour que les secrets ne puissent pas orienter le timing.
Curve25519 et X25519 : de la crypto qui cherche à être difficile à mal utiliser
L’échange de clés sur courbe elliptique permet à deux appareils de créer le même secret partagé alors qu’ils n’envoient que des messages « publics » sur le réseau. Chaque côté génère une valeur privée (gardée secrète) et une valeur publique correspondante (sans danger à envoyer). Après échange des valeurs publiques, les deux côtés combinent leur valeur privée avec la valeur publique de l’autre pour obtenir un même secret partagé. Un eavesdropper voit les valeurs publiques mais ne peut pas de manière réaliste reconstruire le secret partagé : les deux parties peuvent alors dériver des clés symétriques et communiquer en privé.
Pourquoi Curve25519/X25519 sont devenus populaires
Curve25519 est la courbe sous-jacente ; X25519 est la fonction d’échange de clés standardisée, « faites exactement ceci ». Leur attrait tient largement à la sécurité par construction : moins de pièges, moins de choix de paramètres et moins de façons de sélectionner par erreur un réglage non sûr.
Elles sont aussi rapides sur une large gamme de matériel, ce qui compte pour des serveurs qui gèrent beaucoup de connexions et pour des téléphones qui cherchent à économiser de la batterie. La conception favorise des implémentations plus faciles à rendre en temps constant (aide à résister aux attaques temporelles), ce qui réduit le risque qu’un attaquant astucieux extraie des secrets en mesurant de petites différences de performance.
Ce que ça fait — et ce que ça ne fait pas
X25519 vous donne un accord de clé : il aide deux parties à dériver un secret partagé pour du chiffrement symétrique.
Il ne fournit pas l’authentification par lui-même. Si vous exécutez X25519 sans vérifier qui se trouve de l’autre côté (par exemple via certificats, signatures ou clé pré-partagée), vous pouvez toujours être trompé et finir par communiquer en toute sécurité avec la mauvaise partie. Autrement dit : X25519 empêche l’écoute, mais n’empêche pas l’usurpation à lui seul.
L’idée forte de NaCl : moins de choix, moins d’erreurs
NaCl (la « Networking and Cryptography library ») a été conçue autour d’un objectif simple : rendre difficile pour les développeurs d’applications d’assembler par erreur de la cryptographie non sécurisée. Plutôt que d’offrir un buffet d’algorithmes, de modes, de règles de padding et de boutons de configuration, NaCl vous oriente vers un petit ensemble d’opérations haut-niveau déjà câblées de façon sûre.
« box » et « secretbox » comme blocs sûrs
Les API de NaCl sont nommées d’après ce que vous voulez faire, pas d’après les primitives que vous allez assembler.
crypto_box(« box ») : chiffrement authentifié à clé publique. Vous lui fournissez votre clé privée, la clé publique du destinataire, un nonce et un message. Vous obtenez un ciphertext qui (a) cache le message et (b) prouve qu’il vient de quelqu’un qui connaît la bonne clé.crypto_secretbox(« secretbox ») : chiffrement authentifié à clé partagée. Même idée, mais avec une clé secrète partagée.
L’avantage clé est que vous ne choisissez pas séparément « mode de chiffrement » et « algorithme MAC » puis n’espérez pas les avoir combinés correctement. Les valeurs par défaut de NaCl imposent des compositions modernes résistantes aux mauvaises utilisations (encrypt-then-authenticate), si bien que des modes d’échec communs — comme oublier la vérification d’intégrité — sont moins probables.
Le compromis : moins de choix vs. flexibilité
La rigidité de NaCl peut sembler limitante si vous avez besoin de compatibilité avec des protocols hérités, des formats spécialisés ou des algorithmes imposés par la réglementation. Vous échangez « je peux régler chaque paramètre » contre « je peux livrer quelque chose de sûr sans être expert en cryptographie ».
Pour beaucoup de produits, c’est exactement le but : contraindre l’espace de conception pour que moins de bugs puissent exister. Si vous avez vraiment besoin de personnalisation, vous pouvez descendre aux primitives bas-niveau — mais vous vous exposez alors à nouveau aux arêtes vives.
Paramètres par défaut sûrs et le coût des trop nombreux boutons
« Sûr par défaut » signifie que l’option la plus sûre et la plus raisonnable est ce que vous obtenez si vous ne faites rien. Si un développeur installe une bibliothèque, copie un exemple rapide ou utilise les paramètres du framework, le résultat doit être difficile à mal utiliser et difficile à affaiblir accidentellement.
Les valeurs par défaut comptent parce que la plupart des systèmes réels tournent avec elles. Les équipes vont vite, la documentation est survolée et la configuration croît organiquement. Si le défaut est « flexible », cela se traduit souvent par « facile à mal configurer ».
Comment les valeurs par défaut créent silencieusement du risque
Les échecs crypto ne sont pas toujours causés par une « mauvaise math ». Ils viennent souvent du choix d’un réglage dangereux parce qu’il était disponible, familier ou facile.
Pièges courants des valeurs par défaut :
- Hasard faible ou prévisible : utiliser des PRNG non-cryptographiques, réutiliser des seeds, ou retomber sur des sources d’entropie basses dans des conteneurs/VM. Si la génération de clés dépend d’un hasard bancal, tout ce qui en découle hérite de cette faiblesse.
- Algorithmes obsolètes encore supportés pour compatibilité : laisser SHA-1, MD5 ou de vieilles tailles de RSA activés "au cas où", puis découvrir qu’ils sont utilisés en production parce que le système a négocié vers eux.
- Modes et paramètres personnalisés ou inhabituels : offrir beaucoup de boutons pour les modes de chiffrement par bloc, règles de padding, gestion des nonces ou schémas maison. Plus il y a de choix, plus il y a de façons de créer accidentellement un protocole qui ressemble à du chiffrement mais n’est pas sûr.
Règle pratique : moins d’options, résultats plus sûrs
Privilégiez des stacks qui rendent le chemin sûr le plus simple : primitives vetées, paramètres conservateurs, et APIs qui ne vous demandent pas de prendre des décisions fragiles. Si une bibliothèque vous force à choisir entre dix algorithmes, cinq modes et multiples encodages, on vous demande d’ingénier la sécurité par configuration.
Quand c’est possible, choisissez des bibliothèques et des designs qui :
- par défaut utilisent des algorithmes modernes largement revus
- retirent les options dépréciées au lieu de les cacher derrière des « paramètres avancés »
- rendent les opérations dangereuses impossibles (ou au moins douloureuses à activer)
La sécurité par construction, c’est en partie refuser de transformer chaque décision en un menu déroulant.
À quoi ressemble « simple et vérifiable » dans du code réel
« Vérifiable » ne signifie pas « formellement prouvé » dans la plupart des équipes produit. Ça veut dire que vous pouvez bâtir la confiance rapidement, de façon répétable et avec moins de possibilités de mal comprendre ce que fait le code.
Ce que « vérifiable » peut signifier (pratiquement)
Un code devient plus vérifiable quand :
- La lisibilité est élevée : petites fonctions, noms clairs et minimalisme « magique ». Vous pouvez expliquer le flux à un nouvel ingénieur sans un tableau blanc rempli d’exceptions.
- Il existe des vecteurs de test connus : pour une entrée donnée, la sortie est fixe et documentée (critique pour la crypto). Ils attrapent les changements accidentels qui semblent « fonctionner » dans des tests superficiels.
- Les builds sont reproductibles : la même source produit le même binaire, donc vous pouvez confirmer que ce qui tourne est bien ce qui a été revu.
- Les audits sont faisables : pas « bon marché », mais bornés — les auditeurs peuvent couvrir les chemins importants sans se noyer dans les options et états de configuration.
Pourquoi des chemins de code plus simples sont plus faciles à relire
Chaque branche, mode et fonctionnalité optionnelle multiplie ce que les relecteurs doivent raisonner. Des interfaces plus simples réduisent l’ensemble des états possibles, ce qui améliore la qualité de la revue de deux façons :
- Les relecteurs peuvent se concentrer sur quelques flux critiques plutôt que de courir après les cas limites.
- Il est plus facile de remarquer quand quelque chose « cloche » (allocation inattendue, parsing risqué, comparaison sensible au timing).
Un workflow de vérification léger à adopter
Restez ennuyeux et répétable :
- Tests : ajouter des tests unitaires plus des test vectors connus pour chaque primitive utilisée ; exécuter en CI à chaque changement.
- Revue : exiger une checklist orientée sécurité pour les changements touchant clés, hasard, sérialisation et comparaisons.
- Monitoring : logger des raisons d’échec à haut niveau (pas de secrets), alerter sur des pics d’échecs de déchiffrement/vérification, et suivre les versions de dépendances pour savoir quand du code crypto a changé sous vous.
Cette combinaison ne remplace pas l’expertise, mais elle élève le plancher : moins de surprises, détection plus rapide et code que l’on peut réellement analyser.
Où les systèmes cryptographiques échouent encore (même avec de bonnes primitives)
Même si vous choisissez des primitives bien considérées comme X25519 ou une API minimaliste style NaCl « box »/« secretbox », les systèmes cassent encore dans les parties désordonnées : intégration, encodage et opérations. La plupart des incidents réels ne sont pas « la mathématique était fausse » mais « la mathématique a été mal utilisée ».
Pièges d’intégration (les suspects habituels)
Les erreurs de gestion des clés sont fréquentes : réutiliser des clés longue durée là où une clé éphémère est attendue, stocker des clés dans le contrôle de source, ou confondre "clé publique" et "clé secrète" parce qu’elles sont toutes deux des tableaux d’octets.
La mauvaise utilisation des nonces est un récidiviste. Beaucoup de schémas de chiffrement authentifiés exigent un nonce unique par clé. Dupliquer un nonce (souvent via des remises à zéro de compteur, des courses multi-processus ou des hypothèses « suffisamment aléatoires ») et vous pouvez perdre confidentialité ou intégrité.
Les problèmes d’encodage et de parsing créent des échecs silencieux : base64 vs hex, suppression de zéros en tête, endianness incohérente ou acceptation de multiples encodages qui se comparent différemment. Ces bugs peuvent transformer une « signature vérifiée » en « quelque chose d’autre vérifié ».
La gestion des erreurs peut être dangereuse dans les deux sens : retourner des erreurs détaillées qui aident les attaquants, ou ignorer les échecs de vérification et continuer quand même.
Pièges opérationnels qui annulent une bonne crypto
Les secrets fuient via logs, rapports de crash, analyses et endpoints de debug. Les clés se retrouvent aussi dans des sauvegardes, images VM et variables d’environnement partagées trop largement. Parallèlement, les mises à jour de dépendances (ou leur absence) peuvent vous laisser sur une implémentation vulnérable même si la conception était solide.
Liste de mitigation (pour non-cryptographes)
- Traitez les nonces comme une exigence de conception : documentez les règles d’unicité et testez la réutilisation.
- Définissez un encodage canonique unique pour clés/messages ; rejetez tout le reste.
- Échouez fermé : si la vérification échoue, arrêtez et remontez une erreur générique.
- Empêchez les secrets d’apparaître dans les logs ; ajoutez des tests automatisés de masquage des logs.
- Stockez les clés dans un gestionnaire de secrets dédié ; rotatif et limitation des accès.
- Verrouillez et révisez les dépendances crypto ; planifiez mises à jour et audits.
Choisir des approches d’ingénierie crypto pour votre produit
De bonnes primitives ne produisent pas automatiquement un produit sûr. Plus vous exposez de choix — modes, paddings, encodages, adaptations maison — plus vous multipliez les façons pour les équipes de construire accidentellement quelque chose de fragile. Une approche sécurité par construction commence par choisir une voie d’ingénierie qui réduit les points de décision.
Cadre de décision pratique
Utilisez une bibliothèque haut-niveau (APIs one-shot comme « chiffrer ce message pour ce destinataire ») quand :
- Votre équipe n’est pas dédiée à la cryptographie.
- Vous avez besoin de valeurs par défaut sûres (gestion des nonces, authent, formats de clés) plus que de flexibilité.
- Vous voulez minimiser le « glue code » qui peut réintroduire des modes de défaillance.
Composez des primitives bas-niveau (AEADs, hashes, échanges de clés) seulement quand :
- Vous avez un spec de protocole clair et de réelles exigences d’interopérabilité.
- Vous pouvez assigner la responsabilité des revues, vecteurs de test et maintenance long terme.
- Vous pouvez prouver que vous ne réinventez pas un protocole existant.
Règle utile : si votre design doc contient « on choisira le mode plus tard » ou « on fera attention aux nonces », vous avez déjà trop de boutons.
Questions à poser aux vendeurs et équipes internes
Demandez des réponses concrètes, pas du marketing :
- Conception API : l’API rend-elle difficiles les états non sûrs ? Les tailles de nonce, de clé et les choix d’algorithme sont-ils contraints ?
- Valeurs par défaut : que se passe-t-il si un dev ne fournit qu’une clé et du texte clair ? Le chiffrement est-il toujours authentifié (AEAD) ou peut-on accidentellement faire du « chiffrement seulement » ?
- Posture face aux canaux secondaires : quelles opérations sont conçues pour être en temps constant ? Quel modèle de menace est supposé pour timing, cache et fuite de branche ?
- Gestion des clés : comment les clés sont-elles générées, stockées, rotées et écrasées en mémoire ? Les formats de clés sont-ils explicites et versionnés ?
- Audits et maintenance : quand a eu lieu le dernier audit indépendant ? Comment les vulnérabilités sont-elles gérées ? Y a-t-il un changelog des changements pertinents pour la sécurité ?
Hygiène d’ingénierie qui paye
Traitez la crypto comme du code critique pour la sécurité : réduisez la surface API, fixez les versions, ajoutez des known-answer tests, et exécutez du fuzzing sur le parsing/sérialisation. Documentez ce que vous ne supporterez pas (algorithmes, formats legacy) et construisez des migrations plutôt que des « boutons de compatibilité » qui restent pour toujours.
Actions concrètes : appliquer la sécurité par construction cette semaine
La sécurité par construction n’est pas un outil que vous achetez — c’est un ensemble d’habitudes qui rendent certaines catégories de bugs plus difficiles à créer. Le fil conducteur dans l’ingénierie à la DJB : garder les choses suffisamment simples pour être raisonnables, resserrer les interfaces pour contraindre les usages erronés, écrire du code qui se comporte de la même façon même sous attaque, et choisir des valeurs par défaut qui échouent en sécurité.
Les points à garder sur le tableau blanc
- La simplicité est une fonctionnalité de sécurité. Composants plus petits, moins d’états et moins de branches de configuration laissent moins d’endroits pour un comportement surprenant.
- Les interfaces serrées empêchent les « usages créatifs ». Préférez des APIs qui acceptent un format correct plutôt que beaucoup d’entrées « presque correctes ».
- La pensée temps-constant réduit les fuites invisibles. Même si votre primitive est solide, le code autour peut fuiter des secrets via timing, branches ou motifs d’accès mémoire.
- Les bons paramètres par défaut valent mieux que des options sans fin. Chaque bouton ajoute une nouvelle combinaison à tester — et souvent une nouvelle façon de mal configurer.
Liste d’actions d’une semaine pour les équipes
- Inventaire : listez partout où vous faites de la crypto (paramètres TLS, hachage de mots de passe, signature de tokens, échange de clés, génération de nombres aléatoires). Notez la bibliothèque exacte et la configuration en usage.
- Remplacez les patterns risqués : retirez la crypto maison, le parsing/encodage « malin » et les APIs riches en fonctionnalités qui peuvent être mal utilisées. Standardisez sur un petit ensemble de primitives opinionnées et une seule façon de les appeler.
- Contraignez les interfaces : encapsulez les appels crypto derrière un module interne étroit avec une surface minimale (peu de paramètres, types forts, validation claire des entrées).
- Ajoutez des tests pour attraper les régressions : known-answer tests pour les primitives, fuzzing pour les parsers, et vérifications « pas de branche dépendante de secret » sur les chemins chauds.
- Verrouillez les valeurs par défaut : définissez des bases de sécurité dans le code (pas dans des wikis), et exigez une revue explicite pour en dévier.
Si vous voulez une checklist structurée pour ces étapes, pensez à ajouter une page interne « inventaire crypto » à côté de vos docs sécurité (par exemple /security).
Remarque sur la sécurité par construction dans une livraison d’app rapide
Ces idées ne se limitent pas aux bibliothèques crypto — elles s’appliquent à la façon dont vous construisez et déployez le logiciel. Si vous utilisez un workflow de génération rapide (par exemple Koder.ai, où vous créez des apps web/serveur/mobile via chat), les mêmes principes se retrouvent comme contraintes produit : garder un petit nombre de stacks supportés (React pour le web, Go + PostgreSQL pour le backend, Flutter pour le mobile), insister sur la planification avant de générer des changements, et rendre les rollbacks bon marché.
En pratique, des fonctionnalités comme mode planification, snapshots et rollback, et export du code source aident à réduire le "rayon d’explosion" des erreurs : vous pouvez relire l’intention avant le déploiement, revenir rapidement en arrière si quelque chose casse, et vérifier que ce qui tourne correspond à ce qui a été généré. C’est le même instinct de sécurité par construction que la compartimentation de qmail — appliqué aux pipelines de livraison modernes.
FAQ
Que signifie « sécurité par construction » en pratique ?
La sécurité par construction consiste à concevoir un logiciel de sorte que le chemin le plus sûr soit aussi le plus simple. Plutôt que de compter sur des listes de contrôle longues, on contraint le système pour que les erreurs courantes soient difficiles à commettre et que les erreurs inévitables aient un impact limité (un « rayon d’explosion » réduit).
Pourquoi la simplicité réduit-elle le risque de sécurité ?
La complexité crée des interactions cachées et des cas limites difficiles à tester et faciles à mal configurer.
Gains pratiques de la simplicité :
- moins de chemins de code à auditer et à fuzz
- moins de combinaisons de configuration qui peuvent désactiver des protections
- plus facile de raisonner sur les modes de défaillance quand quelque chose tourne mal
Qu’est-ce qu’une « interface serrée » et comment en concevoir une ?
Une interface serrée fait moins de choses et accepte moins de variations. Elle évite les entrées ambiguës et réduit les modes facultatifs qui créent de la « sécurité par configuration ».
Approche pratique :
- autoriser uniquement certains types d’entrée (types, tailles, encodages)
- rejeter les champs inconnus plutôt que d’essayer un "parser tolérant"
- placer les opérations puissantes/dangereuses derrière des points d’entrée séparés et clairement limités
Que peut apprendre qmail aux systèmes modernes sur la limitation du rayon d’explosion ?
qmail divise le traitement du courrier en petits programmes (réception, mise en file, livraison locale, livraison distante, etc.) avec des responsabilités étroites. Cela réduit les modes de défaillance parce que :
- un plantage d’un composant est moins susceptible de corrompre tout le système
- une vulnérabilité dans un composant n’accorde pas automatiquement des privilèges sur tout
- chaque composant est plus facile à tester et à auditer isolément
Qu’est-ce que « temps constant » et pourquoi devrais-je m’en soucier ?
Le comportement en temps constant vise à faire en sorte que la durée d’exécution (et souvent les motifs d’accès mémoire) ne dépendent pas des valeurs secrètes. C’est important parce que des attaquants peuvent parfois déduire des secrets en mesurant le temps, les effets de cache ou les différences "fast path vs slow path" sur de nombreuses exécutions.
Il s’agit d’empêcher des fuites « invisibles », pas seulement de choisir des algorithmes solides.
Comment repérer les risques de fuite par le timing sans lire de l’assembleur ?
Commencez par identifier ce qui est secret (clés privées, secrets partagés, clés MAC, tags d’authentification), puis cherchez les endroits où les secrets influencent le contrôle de flux ou les accès mémoire.
Signaux d’alarme :
- branches
ifsur des données secrètes - accès à des tableaux/indexation basée sur des secrets
- boucles qui sortent tôt en fonction de secrets
- comparaisons qui retournent tôt (égalité non constant-time)
Vérifiez aussi que votre dépendance cryptographique revendique explicitement un comportement en temps constant pour les opérations que vous utilisez.
Pourquoi Curve25519/X25519 sont-ils considérés comme « plus difficiles à mal utiliser » ?
X25519 est une fonction d’accord de clé spécifique construite sur Curve25519. Elle est populaire parce qu’elle réduit les pièges : moins de paramètres à choisir, bonnes performances et une conception qui facilite des implémentations en temps constant.
C’est une voie par défaut plus sûre pour l’échange de clés — à condition de gérer correctement l’authentification et la gestion des clés.
X25519 authentifie-t-il l’autre partie par lui-même ?
Non. X25519 fournit un accord de clé (secret partagé) mais ne prouve pas l’identité de l’autre partie.
Pour empêcher l’usurpation, associez-le à une authentification :
- certificats / signatures (par exemple TLS)
- clé pré-partagée
- schéma de signature au niveau application
Sinon, vous pouvez vous retrouver à « parler en toute sécurité » avec la mauvaise partie.
Quelle est l’idée principale derrière les API « box » et « secretbox » de NaCl ?
NaCl réduit les erreurs en proposant des opérations de haut niveau déjà correctement composées, plutôt qu’un buffet d’algorithmes et de modes.
Briques usuelles :
crypto_box: chiffrement authentifié à clé publique (vous + clé publique du destinataire + nonce → ciphertext)crypto_secretbox: chiffrement authentifié à clé partagée
Le bénéfice pratique est d’éviter des erreurs de composition courantes (par exemple chiffrer sans protection d’intégrité).
Où les systèmes réels échouent-ils même avec de bonnes primitives cryptographiques ?
Même de bons primitives échouent quand l’intégration et l’exploitation sont négligées. Pièges courants :
- réutilisation de nonce (compteurs remis à zéro, courses multi-processus, mauvaises hypothèses de hasard)
- encodages incohérents (hex vs base64, zéros en tête supprimés, endianness)
- gestion des erreurs dangereuse (trop verbeuse ou ignorer les échecs de vérification)
- fuite de clés via logs, rapports de crash, sauvegardes ou variables d’environnement trop larges
Mitigations : documenter l’unicité des nonces et tester leur réutilisation ; imposer un encodage canonique et rejeter le reste ; échouer fermé avec des messages génériques ; stocker les clés dans un gestionnaire de secrets et restreindre/rotater l’accès.