DEVLOPN Audit

Le rôle GCS « lecture seule » qui expose la liste complète de vos fichiers

Sur un bucket public, objectViewer autorise le LIST anonyme complet là où legacyObjectReader le bloque. La différence tient à une permission. Testé en curl.

Un rôle GCS « lecture seule » peut exposer la liste complète de vos fichiers en anonyme. Je l'ai testé, GET et LIST à l'appui, et le rôle le plus récent est le plus bavard.

Le rôle trop bavard : objectViewer expose le LIST complet du bucket en anonyme, legacyObjectReader le bloque

Le test

Un bucket servant des fichiers publics par URL directe. Deux façons de donner la lecture à allUsers :

roles/storage.objectViewer, le rôle moderne recommandé partout :

GET  https://storage.googleapis.com/BUCKET/objet.jpg   → 200
LIST https://storage.googleapis.com/storage/v1/b/BUCKET/o → 200, la liste complète

roles/storage.legacyObjectReader, le rôle « legacy » que la console décourage :

GET  https://storage.googleapis.com/BUCKET/objet.jpg   → 200
LIST https://storage.googleapis.com/storage/v1/b/BUCKET/o → 403

Reproduisez-le vous-même, sans authentification :

curl -s "https://storage.googleapis.com/storage/v1/b/VOTRE_BUCKET/o?maxResults=10"

Si cette commande renvoie du JSON avec vos noms d'objets, n'importe qui sur Internet a la même vue.

La différence : une permission

objectViewer contient storage.objects.list. legacyObjectReader ne contient que storage.objects.get. Tout est là.

Pour un usage interne, lister est un confort. Accordé à allUsers, c'est une capacité d'énumération : la liste des clés révèle souvent des IDs utilisateurs, des noms de documents, des dates, des structures internes. Si vos URLs contiennent un UUID « impossible à deviner », l'obscurité de l'URL était peut-être votre seule barrière : le LIST anonyme la supprime d'un coup.

Les trois réflexes

Pour du public servi par URL directe : legacyObjectReader. Le préfixe « legacy » décourage, à tort. C'est exactement le rôle qui donne le GET sans le LIST. Le rôle le plus récent n'est pas le plus sûr, ici c'est l'inverse.

Pour tout bucket qui ne doit jamais être public : publicAccessPrevention: enforced. C'est un verrou volontaire : désactivable par qui a les droits (sauf policy d'organisation héritée), mais il transforme une erreur d'IAM silencieuse en action délibérée et auditée.

Dans la checklist d'audit : le curl anonyme. GET sur un objet connu, LIST sur le bucket, sans credentials. Dix secondes, et c'est le seul test qui reflète ce que voit réellement un inconnu, indépendamment de ce que la console affiche.

À retenir

  • objectViewer pour allUsers = GET 200 et LIST 200 : liste complète du bucket exposée.
  • legacyObjectReader = GET 200 et LIST 403 : la seule différence est storage.objects.list.
  • Public par URL directe : legacy. Jamais public : publicAccessPrevention en verrou volontaire. Et dans les deux cas : tester en anonyme avec curl.

Vous avez déjà lancé le LIST anonyme sur vos propres buckets ?