# Anatomie d’une élévation de privilège via un shortcode non filtré

> Un shortcode censé afficher un tableau de bord exécutait en réalité une action réservée à l'administrateur, sans le moindre contrôle de capacité.

- Auteur : WordPress Développement
- Publié le : 2023-02-19
- Mis à jour le : 2023-02-19
- Catégorie : Sécurité
- URL : https://www.wpmoderne.fr/securite/elevation-privilege-shortcode-non-filtre/

## L’essentiel

- Un shortcode s'exécute pour tout visiteur qui charge la page
- Lire une variable de requête n'est jamais un contrôle d'accès
- Vérifier la capacité à chaque action, pas seulement à l'affichage

`[tableau_membres action="revoquer" id="118"]` : ce shortcode, découvert dans le contenu d'une page accessible à tout utilisateur connecté d'un site associatif, ne se contentait pas d'afficher une liste. Il exécutait directement l'action décrite dans son attribut, quel que soit le rôle de la personne qui chargeait la page.

L'audit du site a commencé par une question simple posée par un membre du bureau : pourquoi un compte au rôle « contributeur » avait-il pu faire disparaître l'accès d'un autre membre à l'espace privé ? La réponse tenait dans le code du shortcode responsable de l'affichage du tableau de gestion des adhésions.

## Un shortcode qui confond affichage et action

Le handler du shortcode lisait un paramètre transmis dans l'URL de la page, `$_GET['action']`, et l'exécutait immédiatement si sa valeur correspondait à l'une des opérations reconnues : valider une adhésion, révoquer un accès, ou modifier un rôle. Aucune vérification de capacité n'entourait cet appel : le shortcode se comportait comme si le simple fait de charger la page suffisait à en autoriser le contenu.

```
function tableau_membres_shortcode( $atts ) {
    if ( isset( $_GET['action'] ) && 'revoquer' === $_GET['action'] ) {
        $id = absint( $_GET['id'] );
        update_user_meta( $id, 'adhesion_active', false );
    }
    return afficher_tableau_adhesions();
}
add_shortcode( 'tableau_membres', 'tableau_membres_shortcode' );
```

N'importe quel utilisateur connecté, y compris un contributeur sans droit d'administration des membres, pouvait donc révoquer l'adhésion d'un autre en modifiant simplement l'URL de la page où figurait ce shortcode. Aucun clic sur un bouton d'action n'était même nécessaire : visiter le lien suffisait.

## Pourquoi ce défaut a traversé plusieurs mises à jour

Le shortcode avait été développé initialement pour un usage strictement administrateur, sur une page réservée. Lorsqu'un module d'espace membre a été ajouté par la suite, le même shortcode a été réutilisé tel quel sur une page accessible à tous les rôles connectés, sans que personne ne revienne vérifier les contrôles d'accès internes au code. Le contexte d'usage avait changé ; le code, lui, était resté figé sur son hypothèse d'origine.

- Le shortcode ne vérifiait ni le rôle ni la capacité de l'utilisateur courant.
- L'action était déclenchée par simple lecture d'un paramètre d'URL, sans confirmation ni nonce.
- Aucun journal ne consignait qui avait déclenché quelle révocation.

> L'essentiel à retenir : Un shortcode s'exécute pour tout visiteur qui charge la page ; Lire une variable de requête n'est jamais un contrôle d'accès ; Vérifier la capacité à chaque action, pas seulement à l'affichage

## Le correctif tient en trois vérifications

Corriger ce type de faille demande rarement une réécriture complète. Il faut d'abord une vérification de capacité adaptée à l'action réellement effectuée, ensuite un nonce pour éviter qu'un lien forgé ne déclenche l'action à l'insu de l'utilisateur, enfin une journalisation minimale de l'opération.

```
function tableau_membres_shortcode( $atts ) {
    if (
        isset( $_GET['action'], $_GET['id'], $_GET['_wpnonce'] )
        && 'revoquer' === $_GET['action']
        && current_user_can( 'gerer_adhesions' )
        && wp_verify_nonce( $_GET['_wpnonce'], 'revoquer_adhesion' )
    ) {
        $id = absint( $_GET['id'] );
        update_user_meta( $id, 'adhesion_active', false );
        error_log( sprintf( 'Adhésion révoquée : id %d par utilisateur %d', $id, get_current_user_id() ) );
    }
    return afficher_tableau_adhesions();
}
```

La capacité personnalisée `gerer_adhesions`, ajoutée spécifiquement au rôle du bureau associatif via `add_cap()`, remplace ici une vérification de rôle en dur qui aurait été tout aussi fragile à long terme.

## Un principe qui dépasse ce seul shortcode

Ce cas rappelle qu'un shortcode n'est jamais « juste de l'affichage » du point de vue de la sécurité. Dès qu'un shortcode lit une variable de requête pour orienter son comportement, il devient un point d'entrée exécutable au même titre qu'une route REST ou un gestionnaire AJAX, et doit être audité avec la même rigueur.

> Repère utile retenu de cet audit : si un shortcode peut modifier une donnée, il mérite les mêmes contrôles qu'une action d'administration, jamais moins.

## En résumé

Trois lignes de code manquaient pour empêcher cette élévation de privilège : une vérification de capacité, une validation de nonce, une trace d'audit. Leur absence provenait d'un changement de contexte d'usage non anticipé, pas d'une négligence isolée. La vigilance à avoir, pour tout code amené à évoluer, consiste à réévaluer les hypothèses de sécurité chaque fois que le public visé change, même si le code lui-même reste inchangé.
