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>
15 lines
2.3 KiB
Markdown
15 lines
2.3 KiB
Markdown
---
|
|
title: L'API publique répond <strong>sans clé API</strong>
|
|
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
|
|
---
|
|
<p>Constaté le 08/09 sur la préprod, depuis internet, sans authentification :</p>
|
|
<pre>curl "https://api.mymuseum.be/api/Configuration?instanceId=633ee379d9405f32f166f047"
|
|
→ 200, avec le contenu complet et ses traductions</pre>
|
|
<p>La même requête <strong>avec</strong> <code>X-Api-Key</code> renvoie le même 200. <strong>Cause exacte, vérifiée le 08/09</strong> : <code>Get</code> (<code>ConfigurationController.cs:52</code>) et <code>GetDetailAsync</code> (<code>:117</code>) portent un <code>[AllowAnonymous]</code> <em>explicite</em>. 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 (<code>client.dart:54</code>), et le mécanisme existe et fonctionne : <code>ApiKeyAuthenticationHandler</code> + la policy <code>AppReadAccess</code>, déjà utilisée par <code>byPin</code> (<code>:83</code>) et <code>export</code> (<code>:383</code>) du même contrôleur.</p>
|
|
<p><strong>Ce que ça expose</strong> : le contenu éditorial de n'importe quelle instance, à qui connaît un <code>instanceId</code> — 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.</p>
|
|
<p>⚠️ <strong>À vérifier avant de conclure</strong> : le périmètre exact. J'ai testé <code>/api/Configuration</code> ; il faut passer en revue les autres routes que consomment les apps visiteur (<code>/api/Section/configuration/{id}</code>, <code>/api/Resource/{id}</code>, <code>/api/SectionMap</code>, <code>/api/SectionQuiz</code>) avant de savoir si le trou est ponctuel ou général.</p>
|
|
<p>C'est le problème symétrique du 403 sur <code>GET /api/Instance/{id}</code>, <strong>corrigé le 08/09</strong> (voir la carte close) : la même app est <strong>trop bloquée</strong> sur une route et <strong>pas bloquée du tout</strong> sur les autres. Les deux se traitent ensemble, en décidant ce que <code>X-Api-Key</code> est censé garder.</p>
|