Une cartographie précise des défaillances permet d’identifier clairement les responsabilités. Le modèle a généré du texte, mais c’est le backend qui a décidé de l’exécution de POST /v1/tools/refund.create ; c’est également au niveau du backend que l’on peut vérifier le périmètre d’action de l’utilisateur, exiger une validation pour les opérations irréversibles et associer une clé d’idempotence pour éviter les doubles débits lors des tentatives de réexécution. Une fois cette frontière établie, les tentatives d’injection de prompt sont traitées comme des entrées échouant aux étapes de validation et d’autorisation, plutôt que comme des instructions entraînant un transfert de fonds.
Nous supposons une maîtrise pratique des codes d’état HTTP, ce qui implique de comprendre la signification d’une réponse de refus lors des échanges réseau.
Nous supposons une connaissance des bases des JWT, notamment la capacité à vérifier une signature ainsi que l’émetteur, l’audience et la date d’expiration à l’aide d’une bibliothèque éprouvée.
Nous supposons la maîtrise de l’injection de dépendances avec FastAPI, ce qui permet d’intégrer les vérifications d’authentification, d’autorisation et de quotas en tant que dépendances liées à la requête, plutôt que de les disperser au sein des gestionnaires de requêtes (handlers).
L’artefact que nous élaborons tout au long de ce cours est un pipeline backend de référence : chaque étape comporte un point de refus explicite, chaque entrée de journal contient des champs stables facilitant l’analyse, et chaque cas de régression est associé à un mécanisme de contrôle spécifique. L’objectif n’est pas de concevoir un prompt ingénieux, mais de créer un backend capable d’effectuer du RAG (Retrieval-Augmented Generation) en toute sécurité sur l’historique du compte, d’appeler refund.create uniquement lorsque l’utilisateur u_123 y est autorisé et a obtenu l’approbation nécessaire, et d’exécuter des scripts d’analyse sans accorder d’accès au réseau ou aux secrets. Le schéma ci-dessous définit le cheminement de la requête que nous allons sécuriser étape par étape.
Le backend FastAPI d’un copilote d’assistance connaît généralement une défaillance classique. Le backend lit l’historique du compte acct_987, un modèle suggère les étapes suivantes et un appel d’outil déclenche un remboursement pour la transaction txn_456 car le texte du ticket d’assistance semblait convaincant. Rien d’inhabituel ne s’est produit. L’application a accepté du texte non fiable provenant du ticket, a interprété la suggestion comme une autorisation et a exécuté refund.create avec les privilèges du service, au lieu de vérifier les droits de l’utilisateur final et de passer par une étape de validation.
L’aspect sécurité de ce cours consiste à mettre en place les étapes de contrôle manquantes aux seuls endroits où elles peuvent être appliquées efficacement. La formulation du prompt ne peut empêcher un routeur d’outils d’envoyer une requête, et un modèle ne peut prouver que l’utilisateur u_123 est autorisé à rembourser la transaction txn_456. Soit le backend refuse la requête avant l’exécution de l’outil, soit il exécute l’outil et engage des fonds réels.
Nous conservons des références concrètes constantes pour chaque leçon afin que chaque mécanisme de contrôle s’applique aux mêmes éléments du système. Le locataire (tenant) est t_acme, l’utilisateur final est u_123, le compte est acct_987 et la transaction éligible au remboursement est txn_456. Le seul outil de remboursement est nommé refund.create, et la seule route permettant d’y accéder est POST /v1/tools/refund.create.
Nous définissons également l’unique nom de portée (scope) autorisant un remboursement. Une entité (principal) dépourvue du droit support:refund peut toujours discuter des politiques, résumer l’historique du compte et rédiger une réponse, mais elle ne doit pas pouvoir exécuter refund.create. Le choix du modèle reste explicite mais abstrait : l’application appelle un modèle de conversation via $CHAT_MODEL et utilise $MODERATION_MODEL là où une leçon ultérieure nécessitera un appel de filtrage dédié.
Une fois les noms définis, nous pouvons cartographier le cheminement d’une requête et identifier les points d’entrée des données non fiables. Le message de l’appelant est considéré comme non fiable, même s’il provient d’un agent d’assistance authentifié, car il peut contenir du texte copié du client ou le contenu du ticket. Les segments d’historique de compte récupérés sont également non fiables, car la récupération renvoie tout le contenu du stockage, y compris des notes obsolètes et du texte libre susceptible d’influencer le modèle. Les résultats des outils sont non fiables, car les systèmes en aval peuvent renvoyer des chaînes de caractères ou des champs inattendus ; de même, la sortie du modèle est non fiable, car celui-ci privilégie la plausibilité du texte plutôt que la conformité aux règles.
Le cours met en place uniquement les points de contrôle capables de transformer ces entrées non fiables en une action délimitée et auditable. Nous authentifions et associons une entité (principal) de sorte que chaque requête transporte t_acme et u_123 en tant que faits vérifiés par le serveur, et non comme des en-têtes fournis par l’appelant. Nous mesurons les quotas avant l’appel au fournisseur pour éviter qu’un point de terminaison ne consomme en une nuit l’équivalent d’un mois de jetons. Nous autorisons la récupération des données au niveau du point d’appel afin que le backend ne récupère que les segments que u_123 est autorisé à voir, tout en traitant systématiquement chaque segment récupéré comme du contenu non fiable. Nous validons la sortie structurée du modèle avant qu’elle ne soit traitée par un routeur, un analyseur, un modèle de document ou un outil. Nous autorisons et approuvons les appels d’outils pour empêcher l’exécution de refund.create simplement parce que le modèle a formulé une suggestion convaincante. Nous isolons les scripts d’analyse dans un environnement sécurisé (bac à sable) afin que le code généré ne puisse accéder ni au réseau ni aux secrets. Nous auditons chaque refus et chaque action exécutée en consignant suffisamment d’informations pour expliquer qui a agi, pour quel locataire (tenant) et pourquoi le backend a autorisé l’opération.
Lorsque le texte d’un ticket de support tente de déclencher un remboursement, quelle étape aurait pu refuser ce remboursement avant même l’exécution d’un outil ?