Aller au contenu

Denial of wallet sur une application adossée à une API d'IA

Denial of wallet sur une application adossée à une API d’IA

Section intitulée « Denial of wallet sur une application adossée à une API d’IA »

Le denial of wallet (déni de portefeuille) vise la facture plutôt que la disponibilité : un attaquant fait consommer à une application des appels payants vers une API (Application Programming Interface) d’IA (intelligence artificielle) générative jusqu’à épuiser le budget ou déclencher un plafond qui coupe le service. Le coût étant facturé à l’usage, chaque requête abusive se paie en euros, pas seulement en ressources. La position défendue : le quota se compte en jetons consommés par identité, jamais en nombre de requêtes seul.

→ Lire l’explication complète de la marmotte

Mécanique de coût d’une API d’IA générative

Section intitulée « Mécanique de coût d’une API d’IA générative »

Le fournisseur facture en jetons (tokens) : des fragments de texte comptés à l’entrée (le prompt, l’historique, les documents joints) et à la sortie (la réponse générée), avec un tarif de sortie généralement plus élevé que celui d’entrée, et un tarif très variable d’un modèle à l’autre. Conséquence directe : deux requêtes HTTP (HyperText Transfer Protocol) identiques en apparence peuvent différer de plusieurs ordres de grandeur en coût. Un limiteur qui compte des requêtes par minute protège le serveur, pas le budget.

Les leviers d’un attaquant découlent de cette tarification :

  • Gonfler l’entrée : envoyer des documents au maximum de la fenêtre de contexte à chaque appel.
  • Forcer une sortie longue : demander une énumération, une traduction intégrale, une répétition sans fin, pour que la génération s’arrête au plafond de sortie plutôt que naturellement.
  • Choisir le modèle : si le client transmet le nom du modèle et que le serveur le relaie, l’attaquant sélectionne le plus cher.
  • Déclencher des boucles : dans une application agentique, une instruction injectée peut provoquer des appels d’outils en chaîne, chacun renvoyant son résultat dans le contexte de l’appel suivant, donc un coût d’entrée qui croît à chaque tour.
  • Multiplier les identités : ouvrir des comptes gratuits en série quand l’inscription ne coûte rien.

La slide commerciale parle d’« IA à la demande ». Personne ne précise à la demande de qui.

Les contrôles efficaces s’empilent en couches, du plus proche de la requête au plus global.

Bornes par requête, fixées côté serveur. Taille maximale d’entrée, plafond de jetons de sortie (paramètre de type max_tokens selon l’API), modèle imposé par le serveur et jamais lu depuis la requête cliente, nombre maximal d’itérations pour une boucle d’agent. Ces bornes coûtent zéro et suppriment les attaques les plus rentables. Une application qui n’a pas de plafond de sortie laisse l’attaquant fixer la facture.

Budget de jetons par identité. Un compteur par utilisateur ou par locataire, sur une fenêtre glissante (heure, jour), incrémenté avec la consommation réelle renvoyée par le fournisseur dans chaque réponse. Au dépassement, refus explicite ou bascule vers un modèle moins coûteux. Ce budget suppose une identité stable : pour un usage anonyme, il ne reste que l’adresse source, trivialement multipliée ; d’où l’intérêt d’exiger une authentification avant tout appel payant. Le rattachement fiable des requêtes à une identité relève de la rubrique identités.

Plafond global chez le fournisseur. Les consoles des fournisseurs proposent des seuils d’alerte et, selon les offres, des plafonds de dépense. Le comportement au dépassement (blocage immédiat, alerte seule, délai de prise en compte) varie et se vérifie dans les conditions en vigueur. Ce plafond est le dernier filet, pas un contrôle : quand il se déclenche, l’attaquant a obtenu exactement ce qu’il voulait, soit la facture, soit l’indisponibilité pour tout le monde.

Le compromis central est là : un plafond global bas protège le budget au prix d’une coupure totale, un plafond haut garde le service au prix de la facture. Les budgets par identité rendent ce dilemme rare, en arrêtant l’abus avant qu’il atteigne le plafond global.

Exemple : limites de débit par jeton client dans nginx

Section intitulée « Exemple : limites de débit par jeton client dans nginx »

Cet extrait protège l’entrée d’une passerelle interne qui relaie les appels vers le fournisseur. Il ne compte pas les jetons consommés (cette logique vit dans la passerelle, qui lit la consommation renvoyée par le fournisseur), mais il borne la taille, le débit et la concurrence avant tout appel payant.

# Contexte http de nginx.conf
# Clé de limitation : l'en-tête Authorization, disponible avant la phase d'accès.
# Une variable posée par auth_request serait encore vide ici, car limit_req
# s'exécute en phase preaccess, avant auth_request (phase access).
limit_req_zone $http_authorization zone=ia_debit:10m rate=20r/m;
limit_conn_zone $http_authorization zone=ia_conc:10m;
# Disjoncteur global : borne le débit total vers le fournisseur, toutes identités confondues
limit_req_zone $server_name zone=ia_global:1m rate=300r/m;
server {
listen 443 ssl;
server_name passerelle-ia.interne.example;
ssl_certificate /etc/nginx/tls/passerelle-ia.crt;
ssl_certificate_key /etc/nginx/tls/passerelle-ia.key;
location /v1/ {
client_max_body_size 64k; # borne la taille d'entrée, donc les jetons d'entrée
limit_req zone=ia_debit burst=5 nodelay;
limit_req zone=ia_global burst=50;
limit_conn ia_conc 2; # deux générations simultanées par jeton
limit_req_status 429;
limit_conn_status 429;
auth_request /verif-jeton; # jeton invalide : refus, aucun appel payant
proxy_read_timeout 60s; # coupe les générations interminables
proxy_pass http://passerelle_ia_amont;
}
location = /verif-jeton {
internal;
proxy_pass http://fournisseur-identite.interne.example/introspection;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
}

Un attaquant qui fait varier l’en-tête Authorization contourne la limite de débit, mais chaque jeton inventé échoue à auth_request et n’atteint jamais le fournisseur : la limite protège le coût, qui est ce qui compte ici. Le disjoncteur global, lui, borne le pire cas quelle que soit la stratégie.

Les budgets arrêtent l’abus attendu ; la détection repère celui qui passe sous les seuils. Signaux à suivre, par identité et au global :

  • coût par requête anormalement élevé, et surtout ratio sortie sur entrée qui dérive vers le plafond de sortie à chaque appel ;
  • taux élevé de réponses arrêtées par la limite de longueur plutôt que terminées naturellement (le motif d’arrêt figure dans la réponse de la plupart des API) ;
  • concentration : une poignée d’identités qui consomment l’essentiel du budget ;
  • rafales d’inscriptions suivies d’une consommation maximale sur chaque compte.

Ces métriques se collectent dans la passerelle et s’exploitent avec les outils décrits dans observer un modèle de langage en production. Le point faible classique : l’alerte de facturation du fournisseur arrive avec plusieurs heures de retard, donc la détection doit reposer sur les compteurs internes, pas sur la console du fournisseur.

Les quotas ne distinguent pas un utilisateur légitime intensif d’un compte compromis qui consomme dans son budget : c’est la détection d’anomalie qui fait la différence. Ils ne protègent pas non plus contre une clé du fournisseur volée et utilisée hors de la passerelle, sujet distinct traité dans la page sur les clés d’API de la rubrique IA. Enfin, un budget par identité ne vaut que ce que vaut le coût d’obtention d’une identité : une inscription gratuite sans vérification transforme le quota individuel en quota infini.

Sans budget par identité ni plafond de sortie, le seul contrôle restant est le plafond global du fournisseur : quand l’abus l’atteint, l’API refuse les appels de tous les utilisateurs à la fois, et le service reste coupé jusqu’au relèvement du plafond ou au début de la période de facturation suivante. Sans compteur interne, l’enquête ne dispose que de la facture agrégée par clé, qui ne permet ni d’identifier les comptes abusifs ni de les bloquer sélectivement. Sur une offre gratuite, chaque compte créé en série devient une consommation facturée sans aucune recette en face.

Certaines applications utilisent un service d’intelligence artificielle payant, facturé selon la quantité de texte lue et écrite. Le texte est découpé en petits morceaux appelés jetons, et chaque jeton coûte un peu d’argent. Une personne malveillante peut profiter de ce système : elle envoie des textes très longs, demande des réponses sans fin, ou choisit le modèle le plus cher si l’application la laisse faire. Elle ne vole rien directement, mais elle fait grimper la facture de l’entreprise. Si l’entreprise a fixé un montant maximum chez le fournisseur, ce maximum est atteint et le service s’arrête pour tout le monde. Pour l’éviter, on met plusieurs barrières. D’abord, pour chaque demande, le serveur fixe lui-même une longueur maximale de texte envoyé et de réponse, et choisit le modèle. Ensuite, chaque utilisateur, identifié par son compte, reçoit un budget de jetons par heure ou par jour ; au-delà, ses demandes sont refusées. Le serveur limite aussi le nombre de demandes par minute et le nombre de demandes simultanées. Enfin, on surveille la consommation : un compte qui obtient toujours des réponses de longueur maximale ou qui dépense beaucoup plus que les autres doit être examiné. Ces barrières ne fonctionnent que si créer un compte n’est pas gratuit et instantané.

À lire aussi

Pages voisines

: les deux pages se citent l’une l’autre.