Un agent est un appelant que vous ne pouvez pas entièrement prévoir, ce qui en fait exactement l'appelant qui devrait détenir le moindre privilège. Le réflexe avec un nouvel agent est de lui donner un rôle large pour qu'il « fonctionne tout simplement » pendant que vous itérez. Ce réflexe est la façon dont on finit avec un modèle de langage détenant des identifiants capables de lire chaque bucket et de supprimer chaque table, piloté par du texte qu'un attaquant peut influencer. Le moindre privilège a toujours été la règle. Un appelant non déterministe le rend non négociable.

Le changement d'état d'esprit consiste à cesser de considérer l'agent comme votre code et à commencer à le considérer comme un acteur semi-fiable agissant en votre nom. Votre code fait ce que vous avez écrit. Un agent fait ce que le modèle a décidé, et le modèle a décidé sur la base d'une entrée que vous ne contrôlez pas. La frontière IAM est ce qui se dresse entre « l'agent a fait un choix bizarre » et « l'agent a fait un choix bizarre avec des droits d'administrateur ».

Un rôle par outil, pas un rôle par agent

L'anti-pattern le plus courant est un rôle d'exécution unique attaché à tout l'agent, portant l'union de toutes les permissions dont chaque outil pourrait avoir besoin. Ce rôle est désormais le rayon d'impact de tous les outils à la fois. Une injection de prompt qui atteint l'outil le plus faible hérite des permissions de l'outil le plus puissant.

Limitez les permissions à l'outil, pas à l'agent. La Lambda derrière un outil lookup_order reçoit un rôle qui peut lire une seule table et rien d'autre. La fonction derrière send_email reçoit un rôle qui peut appeler SES pour une seule identité vérifiée. Aucun outil ne porte une permission qu'il n'utilise pas, si bien qu'un appel d'outil compromis ne peut faire que le seul travail de cet outil.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "dynamodb:GetItem",
    "Resource": "arn:aws:dynamodb:eu-west-1:111122223333:table/orders",
    "Condition": {
      "ForAllValues:StringEquals": {
        "dynamodb:LeadingKeys": ["${aws:PrincipalTag/tenant}"]
      }
    }
  }]
}

C'est toute la permission de l'outil de recherche de commande : lire un seul élément, d'une seule table, et uniquement les lignes du locataire appelant. Il n'y a rien d'autre qu'une instruction injectée puisse atteindre.

Restreindre davantage le rôle à l'exécution avec des politiques de session

Les rôles par outil sont le plancher. Pour les systèmes multi-locataires, vous voulez aussi que la permission soit limitée à la requête spécifique. Les politiques de session vous permettent de faire cela : quand votre backend assume le rôle de l'outil, il transmet une politique inline qui s'intersecte avec les permissions du rôle pour cette seule session, de sorte qu'une requête agissant pour le locataire A ne puisse pas toucher les données du locataire B même si le rôle couvre techniquement toute la table.

aws sts assume-role \
  --role-arn arn:aws:iam::111122223333:role/order-lookup \
  --role-session-name agent-tenant-a \
  --policy '{"Version":"2012-10-17","Statement":[{
    "Effect":"Allow","Action":"dynamodb:GetItem",
    "Resource":"arn:aws:dynamodb:eu-west-1:111122223333:table/orders",
    "Condition":{"ForAllValues:StringEquals":
      {"dynamodb:LeadingKeys":["tenant-a"]}}}]}'

La permission effective est l'intersection du rôle et de la politique de session, si bien que les identifiants réellement détenus par l'outil sont restreints à cette requête. Les tags de session combinés à une condition de locataire sur le rôle obtiennent le même résultat de façon déclarative. Dans les deux cas, les identifiants remis à l'appel piloté par le modèle constituent le plus petit ensemble qui accomplit la tâche en cours.

Une politique devant les identifiants

IAM décide de ce qu'un principal peut faire. Il ne décide pas si cet agent précis, à ce tour précis, devrait appeler cet outil précis. Cette question d'autorisation a désormais une réponse dédiée sur AWS : Policy dans Amazon Bedrock AgentCore, généralement disponible depuis le début de ce mois, évalue chaque requête agent-vers-outil par rapport à des règles que vous rédigez en langage naturel et qui se compilent en Cedar, appliquées à un AgentCore Gateway avant que l'appel ne se poursuive. Considérez-la comme une couche devant IAM, pas un remplacement : la passerelle décide si l'appel d'outil est autorisé, et un rôle IAM strict décide de l'ampleur limitée des dégâts si c'est le cas.

Journaliser l'assomption de rôle, pas seulement l'appel d'API

Parce que les outils assument des rôles restreints, CloudTrail vous donne une piste d'audit propre : quel rôle a été assumé, avec quel nom de session, pour quelle tâche. Définissez le role-session-name pour qu'il renvoie au tour de l'agent et au locataire, et vous pouvez reconstituer exactement ce qu'un agent a fait et sous quelle autorité. Quand un agent se comporte mal, cette piste fait la différence entre un incident borné que vous pouvez expliquer et un mystère que vous ne pouvez pas.

Ce qu'il faut retenir

Le moindre privilège ne se relâche pas parce que l'appelant est intelligent. Il se resserre, parce que l'appelant est non déterministe et influencé par une entrée non fiable. Donnez à chaque outil son propre rôle étroit, intersectez-le avec une politique de session par requête, placez une couche d'autorisation comme AgentCore Policy devant, et journalisez chaque assomption de rôle. Quand le modèle fait un mauvais choix, et il en fera un, IAM est ce qui garantit que ce mauvais choix reste limité.

À lire ensuite

Le volet approfondi d'IAM et du durcissement des comptes se trouve dans les notes de terrain cloud : sécuriser votre compte AWS, sur ercan.cloud. Le hub est sur ercanermis.com.