# Un musée en ligne exposait la valeur d’assurance de ses œuvres

> 3,2 millions d'euros : c'est la valeur d'assurance d'une toile qu'un visiteur pouvait lire en clair dans le JSON public de la fiche d'œuvre.

- Auteur : WordPress Développement
- Publié le : 2023-05-23
- Mis à jour le : 2023-05-23
- Catégorie : Sécurité
- URL : https://www.wpmoderne.fr/securite/musee-en-ligne-valeur-assurance-oeuvres-exposee/

## L’essentiel

- Un champ sensible mérite une capacité précise, pas un simple booléen
- La sortie REST doit être testée sans authentification avant mise en ligne
- Une meta interne n'est jamais neutre par défaut

Trois virgule deux millions d'euros. C'est la valeur d'assurance d'une toile du dix-neuvième siècle qu'un musée en ligne exposait sans le savoir dans la réponse JSON publique de sa fiche d'œuvre, aux côtés de l'artiste, de la date de création et du descriptif destiné aux visiteurs. Cette donnée n'avait strictement rien à faire devant les yeux du public : elle servait exclusivement à l'équipe de conservation pour ses dossiers d'assurance et de prêt entre institutions.

Le site du musée utilisait un type de contenu personnalisé `oeuvre` pour cataloguer sa collection, avec une vitrine publique construite sur l'API REST de WordPress. La fiche technique interne, incluant la valeur d'assurance, avait été ajoutée comme simple champ personnalisé au même titre que les informations destinées au public, sans distinction de visibilité entre les deux catégories de données.

## Une seule ligne de configuration en cause

Le champ `valeur_assurance` avait été enregistré de cette manière, au moment de l'ajout de la fiche technique de conservation :

```
register_post_meta( 'oeuvre', 'valeur_assurance', array(
    'type'         => 'number',
    'single'       => true,
    'show_in_rest' => true,
) );
```

Le développeur qui avait ajouté ce champ pensait à l'usage immédiat en back-office : afficher la valeur dans l'écran d'édition de la fiche, pour l'équipe de conservation. L'option `show_in_rest` avait été activée par réflexe, sans réaliser qu'elle rendait la donnée disponible sur l'endpoint public `/wp-json/wp/v2/oeuvre`, exactement au même niveau que le titre ou la description destinés aux visiteurs du site.

## Le contrôle de capacité par champ, seule protection fiable

> L'essentiel à retenir : Un champ sensible mérite une capacité précise, pas un simple booléen ; La sortie REST doit être testée sans authentification avant mise en ligne ; Une meta interne n'est jamais neutre par défaut

La correction reprend le même principe que pour tout champ personnalisé sensible exposé en REST : remplacer le booléen simple par un tableau de configuration avec un `auth_callback` explicite, restreint ici à une capacité réservée aux administrateurs de la collection :

```
register_post_meta( 'oeuvre', 'valeur_assurance', array(
    'type'          => 'number',
    'single'        => true,
    'show_in_rest'  => true,
    'auth_callback' => function() {
        return current_user_can( 'manage_options' );
    },
) );
```

Avec ce correctif, un visiteur anonyme continue de recevoir une structure JSON complète pour la fiche d'œuvre, mais le champ `valeur_assurance` y apparaît vide. Seul un compte disposant de la capacité `manage_options`, en pratique l'équipe d'administration du musée, reçoit la valeur réelle lors d'un appel authentifié à l'API.

### Le test qui aurait dû être fait avant la mise en ligne

Un test simple aurait suffi à repérer le problème avant qu'il n'atteigne la production : appeler l'endpoint REST de la fiche d'œuvre sans aucun en-tête d'authentification, exactement comme le ferait n'importe quel visiteur, et lire attentivement chaque champ de la réponse. Ce test doit devenir systématique dès qu'un type de contenu personnalisé mélange des données publiques et des données de gestion interne :

- Lister tous les champs personnalisés du type de contenu, y compris ceux ajoutés « pour plus tard ».
- Appeler l'endpoint REST correspondant sans authentification.
- Comparer chaque valeur retournée à ce qui est réellement affiché sur la vitrine publique du site.

## Étendre l'audit à toute la collection

Une fois le champ `valeur_assurance` corrigé, l'équipe technique a passé en revue l'ensemble des champs personnalisés du type `oeuvre`, révélant deux autres champs dans une situation similaire : une note de restauration confidentielle et un identifiant de prêt inter-musées. Chacun a reçu un `auth_callback` adapté, avec un principe simple retenu pour la suite du projet : tout nouveau champ personnalisé destiné à un usage interne doit désormais déclarer explicitement sa restriction d'accès dès sa création, pas après un signalement.

## Documenter la distinction entre champ public et champ interne

Au-delà du correctif technique, l'incident a conduit le musée à formaliser une convention de nommage pour ses futurs champs personnalisés : tout champ préfixé `interne_` déclenche automatiquement, dans le gabarit de code utilisé par l'équipe, l'ajout d'un `auth_callback` restrictif par défaut. Cette convention réduit le risque qu'un développeur, pressé par un délai de mise en ligne d'une nouvelle exposition, oublie à nouveau cette étape.

> Un champ personnalisé n'est jamais neutre par défaut : chaque nouveau champ mérite qu'on se demande explicitement qui doit pouvoir le lire, avant même de se demander comment l'afficher.

## En résumé

La valeur d'assurance d'une œuvre n'a rien d'une donnée technique anodine : sa divulgation publique peut avoir des conséquences concrètes, du ciblage de vol à la négociation d'un prêt entre institutions. Ce cas rappelle qu'aucune donnée ajoutée à un type de contenu exposé en REST ne devrait être considérée comme sûre par défaut, quelle que soit la discrétion apparente de son usage prévu en interne.
