Le WordPress d'aujourd'hui, décodé pour les développeurs

Sécurité

Une plateforme e-learning : le certificat prévisible à usurper

Un identifiant séquentiel dans l'URL d'une attestation de formation permettait d'accéder au certificat de n'importe quel autre apprenant. Diagnostic et correctif par jeton non séquentiel.

Par WordPress Développement • 1 août 2022 • 4 min de lecture • Aucun commentaire
Une plateforme e-learning : le certificat prévisible à usurper

/attestation/télécharger?id=4127 : cette URL, générée automatiquement à la fin de chaque module de formation sur une plateforme e-learning intégrée à WordPress, permettait à l’apprenant de télécharger son certificat de réussite. En remplaçant simplement le nombre par 4128, un testeur a obtenu, sans aucune authentification supplémentaire, l’attestation nominative d’un autre apprenant complet, avec son nom, la formation suivie et la date d’obtention.

Cette découverte, faite dans le cadre d’une revue de sécurité demandée avant le renouvellement d’un partenariat avec une grande entreprise cliente de la plateforme, a rapidement révélé l’ampleur du problème : l’identifiant utilisé dans l’URL correspondait directement à la clé primaire auto-incrémentée de la table stockant les attestations en base de données.

Symptôme : une énumération totale du système

En testant simplement une plage de valeurs numériques consécutives, il devenait possible d’accéder à l’intégralité des attestations générées depuis le lancement de la plateforme, soit plus de huit mille trois cents documents à cette date. Aucune vérification de session, de propriété du document ou de droit d’accès n’intervenait entre la réception de la requête et le renvoi du fichier PDF correspondant.

La gravité de ce type de faille tient à sa simplicité d’exploitation : aucune connaissance technique particulière n’est nécessaire, un simple script itérant sur une plage de nombres suffit à collecter l’ensemble des documents, sans jamais déclencher d’alerte particulière côté serveur, chaque requête individuelle étant parfaitement valide du point de vue du code applicatif.

Diagnostic : une confusion entre secret et identifiant technique

La clé primaire d’une table en base de données répond à un besoin purement technique : identifier une ligne de manière unique et efficace pour les opérations de jointure et d’indexation. Elle n’a jamais été conçue pour jouer un second rôle de jeton d’accès, et sa nature séquentielle, un avantage pour les performances de la base de données, devient une faiblesse dès qu’elle est exposée telle quelle dans une URL destinée à contrôler un accès.

Ce type de faille porte un nom bien identifié dans la littérature de sécurité applicative : la référence directe non sécurisée à un objet (Insecure Direct Object Reference), l’une des catégories les plus documentées et les plus fréquemment rencontrées dans les audits d’applications web, WordPress compris.

L'essentiel à retenir : Un identifiant séquentiel transforme une URL privée en énumération triviale ; Un jeton aléatoire suffisant en longueur élimine ce risque sans changer l'architecture ; Le contrôle d'accès doit rester indépendant de la difficulté à deviner l'URL

Correctif : un jeton non séquentiel et vérifié

La correction a introduit un jeton d’accès distinct de la clé primaire, généré aléatoirement à la création de chaque attestation et stocké dans un champ dédié, jamais réutilisé et impossible à deviner en enchaînant des valeurs consécutives :

function generer_jeton_attestation() {
    return bin2hex(random_bytes(16));
}

function creer_attestation($utilisateur_id, $formation_id) {
    global $wpdb;

    $jeton = generer_jeton_attestation();

    $wpdb->insert('wp_attestations', [
        'utilisateur_id' => $utilisateur_id,
        'formation_id'   => $formation_id,
        'jeton_acces'    => $jeton,
        'date_creation'  => current_time('mysql'),
    ]);

    return $jeton;
}

L’URL de téléchargement utilise désormais ce jeton plutôt que l’identifiant numérique, et la route de téléchargement vérifie en complément que l’utilisateur connecté correspond bien au propriétaire de l’attestation, plutôt que de se reposer uniquement sur la difficulté à deviner le jeton :

function telecharger_attestation(WP_REST_Request $request) {
    global $wpdb;
    $jeton = sanitize_text_field($request->get_param('jeton'));

    $attestation = $wpdb->get_row($wpdb->prepare(
        "SELECT * FROM wp_attestations WHERE jeton_acces = %s",
        $jeton
    ));

    if (!$attestation || (int) $attestation->utilisateur_id !== get_current_user_id()) {
        return new WP_Error('acces_refuse', 'Accès non autorisé.', ['status' => 403]);
    }

    return generer_pdf_attestation($attestation);
}

Prévention : deux couches, pas une seule

Le choix de vérifier à la fois le jeton et la correspondance avec l’utilisateur connecté n’est pas redondant : il protège contre deux scénarios distincts. Le jeton non séquentiel empêche l’énumération en masse ; la vérification de propriété protège contre le cas où un jeton aurait fuité par un canal tiers (partage accidentel d’un lien, historique de navigateur partagé), ce qu’un jeton seul, aussi long soit-il, ne peut pas couvrir à lui seul.

En résumé

Toute donnée accessible via une simple modification de paramètre dans une URL doit être considérée comme potentiellement accessible par n’importe qui, indépendamment de la difficulté apparente à deviner sa valeur exacte. La correction reste simple : ne jamais utiliser une clé primaire séquentielle comme identifiant d’accès public, et toujours doubler un jeton imprévisible d’une vérification explicite de propriété côté serveur.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi