Clos : GET /api/Instance/{id} renvoyait 403 à l'app visiteur. Une clé API donne
maintenant accès à sa seule instance, en vue réduite — plan, quotas, TVA,
facturation et pinCode retirés. Déployé en version-3.1.3 et vérifié en préprod
sur cinq cas d'autorisation.
La carte close garde le piège qui vaut pour tout le projet : le test « est-ce un
utilisateur du manager » ne peut pas se baser sur un claim de permission, parce
qu'AuthorizationMiddleware peuple HttpContext.User depuis le schéma ApiKey avant
de court-circuiter sur [AllowAnonymous]. Et elle rectifie une affirmation fausse
de la première rédaction : StripeCustomerId n'est pas exposé par ToDTO.
Nouveau, en Urgent : GET /api/Configuration répond 200 sans aucune clé. Ce n'est
pas une clé mal vérifiée mais un [AllowAnonymous] explicite, probablement hérité
de la v2. Le périmètre exact reste à établir — les autres routes que consomment
les apps visiteur n'ont pas été passées en revue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2.3 KiB
title, area, tags, flag, src
| title | area | tags | flag | src |
|---|---|---|---|---|
| L'API publique répond <strong>sans clé API</strong> | backend | manager-service, sécurité | critical | Le contenu de n'importe quelle instance est lisible par qui connaît son identifiant | 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.