Huit constats de sécurité un samedi matin
Les audits font apparaître des choses. Le geste honnête est de les accueillir, de tout corriger, et de livrer des garde-fous permanents — c'est exactement comme un runtime gagne la confiance de l'entreprise.
L’audit est arrivé dans la boîte de réception à la fin de la semaine dernière. Samedi matin, j’avais le rapport ouvert sur un écran et la base de code ouverte sur l’autre. Huit constats critiques ou élevés. Le genre de contenu de boîte de réception qui définit un week-end.
C’était une due diligence entreprise. Un partenaire potentiel avait demandé à son équipe de sécurité de faire une passe sur la surface API publique avant de laisser ses avocats avancer. Les constats étaient précis, bien expliqués, et entièrement justes. C’étaient aussi le genre de choses auxquelles un flux de travail piloté par agents vous rend particulièrement vulnérable si vous n’êtes pas délibéré à vous en défendre.
À la fin du samedi, chaque constat avait une PR qui le livrait. Voici le billet sur ce qu’était chacun.
Quelles étaient les catégories
Je vais parler de catégories plutôt que de détails d’exploit spécifiques parce que les détails sont maintenant corrigés et sans intérêt pour un concurrent ; les catégories sont ce contre quoi d’autres équipes voudront se défendre.
Un : validation d’entrée sur la surface REST publique. Plusieurs endpoints faisaient plus confiance à leurs entrées qu’ils n’auraient dû. Précisément, des bornes de longueur sur les entrées de chaîne, des bornes de plage sur les entrées numériques, et une validation structurelle sur les charges utiles JSON. Aucune de la validation manquante n’était catastrophique en soi. La combinaison de « pas de borne de longueur ici plus pas de limite de débit là plus pas de vérification d’auth sur cet endpoint admin » est le genre de pile qui devient catastrophique une fois qu’un attaquant a découvert la surface.
Deux : un identifiant codé en dur dans un fichier de configuration. Différent du constat HMAC de quelques week-ends auparavant. C’était un modèle de configuration qui avait un vrai identifiant committé dans la version d’exemple. L’intention avait été de livrer un identifiant d’exemple qui était un placeholder ; ce qui a été livré était un identifiant d’exemple qui était une vraie valeur venant de l’environnement local d’un développeur. Rotation, retrait de l’exemple, ajout à la passe CI de scan de secrets.
Trois : négociation de suite de chiffrement non sécurisée. Un sous-système qui négocie TLS avec un service externe acceptait des suites de chiffrement qui ne devraient pas être acceptables en 2026. Précisément, plusieurs suites pré-TLS-1.3 avec des faiblesses connues. Le correctif a été de contraindre la liste de suites de chiffrement à un ensemble TLS-1.3 uniquement, avec une échappatoire documentée (un drapeau explicite) pour tester contre des systèmes partenaires hérités.
Quatre : un use-after-free à la frontière de l’API C. L’API C de l’une des DLL du runtime avait une fonction qui retournait un pointeur vers un état interne, que l’appelant pouvait continuer à utiliser après que l’état interne ait été libéré par un appel ultérieur sur le même handle. Piège classique d’API C. Le correctif a été de passer d’une sémantique « pointeur détenu par l’appelant » à une sémantique « handle opaque détenu par l’appelant avec un accesseur get-by-handle ». L’accesseur retourne une copie de pointeur fraîche pour la durée de l’appel et la mémoire sous-jacente n’est pas exposée.
Cinq : un trou de thread-safety dans la couche de licence. Deux threads pouvaient entrer en course sur le même objet d’état de licence lors d’appels de vérification concurrents. Sous charge, un thread pouvait voir un état partiellement mis à jour et arriver à des conclusions incorrectes sur la validité de la licence. Le correctif a été un vrai verrou lecture-écriture autour de l’état, avec le chemin de vérification optimisé pour le cas courant (licence valide, aucun changement d’état nécessaire).
Six : un contournement d’autorisation sur un endpoint admin. L’un des endpoints admin avait une vérification d’authentification mais pas de vérification d’autorisation. Tout utilisateur authentifié pouvait l’appeler, y compris les utilisateurs sans le rôle admin. Le correctif a été d’ajouter la vérification de rôle, d’écrire un test unitaire qui exerce à la fois le chemin « authentifié-en-tant-qu’admin autorisé » et « authentifié-mais-pas-admin refusé », et d’auditer chaque autre endpoint admin pour le même motif. Trois autres endpoints avaient le même problème. Les quatre sont maintenant corrects.
Sept : journalisation insuffisante sur les échecs d’authentification. Quand un utilisateur essayait de s’authentifier et échouait, le runtime journalisait la tentative mais ne journalisait pas assez de contexte pour enquêter sur un motif de force brute ou de credential stuffing. Le correctif a été d’ajouter une journalisation structurée sur chaque échec d’auth avec l’adresse IP, le compteur limité en débit pour cette IP, et la méthode d’auth tentée. Préservant la vie privée (aucun contenu de mot de passe n’est journalisé), mais assez de contexte pour enquêter quand une enquête devient nécessaire.
Huit : retard de mise à jour des dépendances. Plusieurs des dépendances tierces que le runtime intègre avaient des versions à vulnérabilité connue épinglées dans le manifeste. Le correctif a été de mettre à jour chacune vers la dernière version corrigée, d’exécuter la suite de tests contre les mises à jour, et de résoudre le petit nombre de changements de forme d’API que les mises à jour nécessitaient. Deux des huit mises à jour nécessitaient des changements d’adaptateur dans notre code ; les six autres étaient directement compatibles. Dependabot est maintenant configuré pour signaler cela automatiquement.
Ce que j’ai appris d’un week-end de travail de sécurité
Trois choses. Pas surprenantes. Qui valent la peine d’être dites.
Les constats de sécurité se regroupent. Huit constats, c’est beaucoup à faire apparaître d’un coup. Le schéma, en regardant en arrière, est qu’ils partagent tous une racine commune : le runtime avait grandi vite dans un flux de travail piloté par agents, avec les agents implémentant des choses correctes-en-isolation. Les préoccupations transversales comme la sécurité sont exactement le genre de chose qui échappe à la relecture PR par PR. L’audit attrape ce que la relecture a manqué. Programmez les audits.
Certains constats sont des modes d’échec pilotés par agents. Le use-after-free dans l’API C est le genre de chose qu’un agent écrit quand le prompt dit « exposez cet état interne à l’appelant » sans spécifier la sémantique de propriété. Le trou de thread-safety dans la couche de licence est similaire. Les deux attrapent le même motif : les agents à qui on ne demande pas la propriété et la concurrence produiront du code qui ignore les deux.
Certains constats sont des modes d’échec pilotés par le rythme. Le retard de mise à jour des dépendances n’est pas un problème d’agent. C’est un problème de « livrer vite et ne pas avoir une personne dont le travail est de garder les dépendances fraîches ». Le correctif est un processus (Dependabot) et une discipline (agir sur les alertes de Dependabot). Le correctif n’est pas « soyez plus intelligent ».
Les nouvelles défenses
Des garde-fous permanents ont atterri ce week-end.
Une passe de scanner de sécurité en CI. Analyse statique pour la validation d’entrée, le scan de secrets, et la politique de suites de chiffrement. Chaque PR exécute le scan. Les PR qui introduisent de nouveaux constats sont signalées pour relecture.
Un motif de test d’endpoint admin. Chaque endpoint admin dans l’API a maintenant un test jumelé qui exerce à la fois le chemin admin-autorisé et le chemin non-admin-refusé. Le motif est appliqué par une vérification CI qui scanne les décorateurs @admin_required et fait échouer le build si le décorateur est présent sans test jumelé correspondant.
Une annotation de thread-safety sur l’état partagé. Chaque objet d’état partagé dans le runtime a maintenant une annotation explicite sur son modèle de concurrence : immuable, protégé par verrou, sans verrou avec happens-before explicite, ou mono-thread. L’annotation fait partie du type. Le code qui touche l’état doit satisfaire l’annotation. Le compilateur l’applique pour les cas protégés par verrou et immuables ; le reste repose sur la relecture.
Un audit de sécurité mensuel au calendrier. Sans attendre qu’un partenaire le demande. L’audit est un point permanent le deuxième samedi de chaque mois. Le premier s’exécute en avril.
Ce que partenaires et bâtisseurs devraient en retenir
Si vous évaluez un moteur pour un partenariat et que vous êtes sur le point de demander un audit, cette équipe l’accueillera. Les audits font apparaître des choses. Les choses se corrigent. La base de code s’améliore. La relation avec le partenaire s’améliore. La discipline d’accueillir l’audit compte plus que n’importe quel constat spécifique.
Si vous êtes un professionnel de la sécurité entreprise qui lit ceci, je suis ouvert aux suggestions sur la cadence d’audit et sur les choses spécifiques que vous souhaiteriez voir plus d’équipes moteur auditer. Les constats de ce week-end étaient les évidents. Les non évidents sont les prochains que je veux trouver.
Si vous exécutez un flux de travail piloté par agents et que vous n’avez pas fait d’audit de sécurité récemment, faites-en un. Le motif « l’agent écrit du code correct-en-isolation qui manque les préoccupations transversales » est universel dans cette forme de flux de travail. L’audit est comment vous attrapez les préoccupations transversales. Je n’ai trouvé aucune autre façon.
Vérification de fin de week-end. Huit constats clos. Six nouvelles défenses livrées. Le prochain audit est au calendrier. La relation avec le partenaire avance.
Retour à la construction.
Un runtime spatial qui passe la due diligence
RakuAI est construit pour les partenaires entreprise et les fabricants de lunettes intelligentes — audité, endurci, et honnête à ce sujet. Voyez comment nous concevons pour la barre de sécurité que votre équipe doit franchir.