--- title: L'API publique répond sans clé API area: backend tags: manager-service, sécurité flag: critical | Le contenu de n'importe quelle instance est lisible par qui connaît son identifiant src: conversation 08/09 — vérification de la préprod depuis internet ---
Constaté le 08/09 sur la préprod, depuis internet, sans authentification :
curl "https://api.mymuseum.be/api/Configuration?instanceId=633ee379d9405f32f166f047" → 200, avec le contenu complet et ses traductions
La même requête avec X-Api-Key renvoie le même 200. Cause exacte, vérifiée le 08/09 : Get (ConfigurationController.cs:52) et GetDetailAsync (:117) portent un [AllowAnonymous] explicite. Ce n'est donc pas une clé mal vérifiée, c'est une ouverture assumée — probablement héritée de la v2, où les apps visiteur n'avaient pas de clé. Les apps l'envoient pourtant (client.dart:54), et le mécanisme existe et fonctionne : ApiKeyAuthenticationHandler + la policy AppReadAccess, déjà utilisée par byPin (:83) et export (:383) du même contrôleur.
Ce que ça expose : le contenu éditorial de n'importe quelle instance, à qui connaît un instanceId — lequel se lit en clair dans un APK de flavor, ou se devine à partir d'un export. Pas de données personnelles, mais tout le travail éditorial d'un client.
⚠️ À vérifier avant de conclure : le périmètre exact. J'ai testé /api/Configuration ; il faut passer en revue les autres routes que consomment les apps visiteur (/api/Section/configuration/{id}, /api/Resource/{id}, /api/SectionMap, /api/SectionQuiz) avant de savoir si le trou est ponctuel ou général.
C'est le problème symétrique du 403 sur GET /api/Instance/{id}, corrigé le 08/09 (voir la carte close) : la même app est trop bloquée sur une route et pas bloquée du tout sur les autres. Les deux se traitent ensemble, en décidant ce que X-Api-Key est censé garder.