S3 retourne 403 Access Denied quand n’importe laquelle d’environ six couches d’évaluation refuse la requête — ou quand l’objet n’existe pas et que votre appelant n’a pas s3:ListBucket, parce que S3 cache volontairement les clés manquantes aux identités qui ne peuvent pas lister. La bonne approche n’est pas de commencer à modifier des politiques au hasard : c’est de parcourir les couches dans un ordre fixe — l’identité appelante, puis la politique IAM, la politique de bucket, Block Public Access, la propriété des objets, et enfin KMS. Une d’entre elles est la réponse, et l’ordre ci-dessous est trié selon la fréquence à laquelle chacune s’avère être la bonne.
Ce qui se passe
Chaque requête S3 passe par la même évaluation. Un Deny explicite n’importe où gagne immédiatement — politique de bucket, politique IAM, SCP, permissions boundary, politique de session ou politique de point de terminaison VPC. Si rien ne refuse, l’appelant a quand même besoin d’au moins un Allow : de sa propre politique IAM quand l’appelant et le bucket sont dans le même compte, ou des deux côtés (politique IAM de l’appelant et politique de bucket) quand ils sont dans des comptes différents. Par-dessus s’ajoutent des barrières qu’on oublie : les politiques de contrôle de service (SCP) au niveau de l’organisation, la politique du point de terminaison VPC par lequel le trafic passe, Block Public Access qui écrase tout octroi public, et — pour les objets chiffrés en SSE-KMS — la politique de la clé KMS, évaluée séparément et qui produit exactement la même chaîne AccessDenied quand elle échoue.
Deux cas particuliers se déguisent en problèmes de permissions. D’abord, l’objet manquant : sans s3:ListBucket sur le bucket, un GET sur une clé qui n’existe pas retourne 403, pas 404. Ensuite, les buckets en mode requester-pays retournent 403 si l’appelant n’envoie pas --request-payer requester.
Quoi vérifier en premier
- Qui appelle réellement? Le profil CLI, le rôle d’instance ou le rôle d’exécution Lambda n’est souvent pas l’identité avec laquelle vous pensez tester.
- Est-ce que l’objet existe vraiment, avec cette clé exacte, dans ce bucket et cette région exacts? Les clés sont sensibles à la casse et une barre oblique initiale fait partie de la clé.
- L’objet est-il chiffré en SSE-KMS? Si oui, la moitié de vos suspects sont du côté KMS.
- Le trafic traverse-t-il un point de terminaison VPC ou une frontière d’organisation? Les deux ajoutent des politiques à l’évaluation.
- Qu’est-ce qui a changé récemment? Un rôle modifié, une nouvelle SCP, une politique de bucket resserrée — regardez CloudTrail avant toute autre chose si l’accès fonctionnait hier.
Comment vérifier
Établissez d’abord les faits sur l’identité et l’objet :
# Qui suis-je, vraiment?
aws sts get-caller-identity
# L'objet existe-t-il et quel chiffrement porte-t-il?
aws s3api head-object --bucket my-bucket --key path/to/file.pdf
# Regardez ServerSideEncryption et SSEKMSKeyId dans la sortie
Puis tirez les couches une à la fois :
aws s3api get-bucket-policy --bucket my-bucket --query Policy --output text
aws s3api get-public-access-block --bucket my-bucket
aws s3api get-bucket-ownership-controls --bucket my-bucket
aws s3api get-bucket-acl --bucket my-bucket
Dans la console, tout ce qui compte vit sous S3 > Buckets > votre-bucket > Permissions. Pour la réponse qui fait autorité sur quelle couche a refusé, CloudTrail vaut mieux que deviner — les événements de données du bucket (s’ils sont activés) montrent le errorCode et, depuis les améliorations de 2023, les messages AccessDenied de la plupart des services indiquent quel type de politique a refusé. Le simulateur de politiques IAM (IAM > Policy simulator) est utile pour les cas dans le même compte, mais il n’évalue pas les politiques de bucket pour les appelants d’un autre compte — son verdict est partiel.
Le correctif
- Mauvaise identité.
get-caller-identitymontre un compte ou un rôle différent de celui attendu — corrigez le profil, l’attachement du rôle ou la chaîne d’identifiants avant de toucher à une politique. Ça règle une bonne part des billets à soi seul. - L’erreur du
/*dans la politique IAM. Les actions sur les objets (s3:GetObject,s3:PutObject) exigent l’ARN d’objetarn:aws:s3:::my-bucket/*;s3:ListBucketexige l’ARN du bucketarn:aws:s3:::my-bucket. Une politique qui n’a qu’un des deux produit des 403 qui semblent aléatoires. - Un deny explicite dans la politique de bucket. Cherchez
"Effect": "Deny"avec des conditions commeaws:SecureTransport,aws:SourceVpceouaws:PrincipalOrgID— un deny TLS-seulement bloque les appels HTTP en clair, une condition de point de terminaison VPC bloque tout ce qui arrive par l’internet public, incluant votre portable. - KMS, pas S3. Pour les objets SSE-KMS, l’appelant a besoin de
kms:Decryptsur la clé et la politique de la clé ne doit pas l’exclure. Les lectures entre comptes exigent que la politique de clé accorde explicitement le compte externe. L’erreur dit quand mêmeAccessDeniedsur l’appel S3, et c’est pourquoi celle-ci se cache si bien. - Propriété des objets entre comptes. Dans les buckets où les ACL sont encore activées, un objet téléversé par un autre compte peut être illisible même par le propriétaire du bucket. Mettez Object Ownership à Bucket owner enforced pour éliminer cette classe de problèmes pour de bon.
- Block Public Access qui fait sa job. Si l’accès attendu était public ou basé sur une ACL, BPA au niveau du bucket ou du compte l’écrase silencieusement. Décidez si cet accès devrait exister avant d’assouplir quoi que ce soit.
- Une SCP ou une permissions boundary. Si la même politique fonctionne dans un compte mais pas dans un autre, la couche organisation est le suspect.
- Le 403 qui est un 404. Accordez
s3:ListBucket(ou testez avec une identité qui l’a) et réessayez — si ça devientNoSuchKey, vous chassiez un fantôme de permissions.
Quand c’est la mauvaise approche
Si le « correctif » vers lequel vous tendez est "Principal": "*", s3:* sur toutes les ressources, ou désactiver Block Public Access, arrêtez. Ça fait disparaître le symptôme en faisant du bucket la prochaine manchette de fuite de données. Un 403 pendant un incident sur des données que vous ne comprenez pas est aussi un signal de ralentir — confirmez ce que le bucket contient avant d’élargir l’accès sous pression.
Erreurs fréquentes
- Tester avec des identifiants administrateur, voir que ça marche, puis livrer du code qui roule sous un rôle beaucoup plus étroit.
- Modifier trois politiques d’un coup : quand le 403 disparaît, personne ne sait quel changement a compté — ni lesquels sont maintenant des octrois inutiles.
- Oublier que la politique de session d’un rôle assumé et sa politique d’identité contraignent toutes deux la session.
- Blâmer la politique de bucket quand l’objet est en SSE-KMS et que la politique de clé est la vraie barrière.
Prévention
- Mettez Object Ownership : Bucket owner enforced sur chaque bucket ; les ACL sont une couche héritée que vous ne voulez pas dans l’évaluation.
- Activez les événements de données CloudTrail sur les buckets sensibles pour que les 403 arrivent avec de la forensique plutôt que des devinettes.
- Faites rouler IAM Access Analyzer pour voir l’accès externe que les politiques accordent réellement, pas celui que vous croyez qu’elles accordent.
- Standardisez une déclaration deny TLS-seulement et testez-la en staging avant qu’elle atterrisse dans les politiques de bucket en production.
- Gardez un ordre de débogage documenté (celui-ci) dans le runbook pour que personne ne commence à l’étape six. Le modèle complet de permissions est documenté sur https://docs.aws.amazon.com/AmazonS3/ si vous avez besoin des détails fins.
Un 403 a toujours exactement une première cause, et les couches sont énumérables — le débogage semble sans fin seulement quand on les sonde dans le désordre. Si personne dans votre équipe ne possède vraiment l’environnement AWS de bout en bout, c’est la racine de la plupart de ces problèmes — Stack Harbor opère des comptes AWS au quotidien sous Opérations AWS, et vous pouvez réserver une revue AWS.