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

Sécurité

Une validation d’identité contournable sur un rendez-vous notarial

Un champ « pièce d'identité vérifiée » coché côté navigateur ne prouve rien côté serveur. Retour sur un audit qui a mis ce défaut à nu.

Par Clément Hadrot • 19 juillet 2022 • 5 min de lecture • Aucun commentaire
Une validation d'identité contournable sur un rendez-vous notarial

Une case cochée « identité vérifiée » suffit-elle à prouver qu’un contrôle a réellement eu lieu ? Sur un formulaire de prise de rendez-vous destiné à des actes nécessitant une vérification préalable, la réponse dépend entièrement de l’endroit où cette vérification s’exécute. Si elle vit uniquement dans le navigateur, elle n’existe pas.

C’est ce qu’a révélé l’audit d’un cabinet notarial équipé d’un module WordPress personnalisé pour la prise de rendez-vous. Le formulaire imposait un téléversement de pièce d’identité avant de proposer des créneaux, avec un champ caché identity_checked positionné à 1 une fois le document accepté par un script de contrôle exécuté… en JavaScript, dans le navigateur du visiteur.

Un contrôle qui ne quitte jamais le poste client

Le script vérifiait la présence d’un fichier, son extension et sa taille, puis activait un bouton « Confirmer » et remplissait un champ masqué transmis avec le reste du formulaire. Rien de plus. Aucune route REST ni aucun traitement PHP ne revalidait cette information à la réception : le contrôleur du formulaire lisait directement la valeur de identity_checked et l’enregistrait telle quelle en tant que méta de la demande de rendez-vous.

Un outil comme les outils de développement du navigateur suffit à modifier cette valeur avant l’envoi. Pas besoin de compétences offensives particulières : ouvrir l’inspecteur, éditer l’attribut value de l’élément caché, soumettre. Le serveur, de son côté, n’a jamais eu la moindre preuve qu’un document avait été fourni.

Pourquoi ce défaut passe inaperçu en recette

Le parcours fonctionnel classique — remplir, téléverser, valider — masque le problème parce qu’il suit toujours le chemin prévu. Les tests manuels reproduisent le comportement attendu de l’interface, jamais une manipulation directe de la requête HTTP sortante. Ce type de faille ne se voit qu’en observant la charge utile réellement transmise au serveur, indépendamment de ce que l’écran affiche.

  • Le champ caché portait un nom explicite, ce qui facilitait sa localisation pour un attaquant curieux.
  • Aucune valeur de contrôle n’était liée à une session ou à un jeton unique côté serveur.
  • Le traitement PHP faisait une confiance totale à toute donnée présente dans $_POST.
L'essentiel à retenir : Un champ caché ne remplace jamais une vérification serveur ; JavaScript désactivé, tout devient falsifiable ; Consigner l'état du contrôle, pas seulement son résultat

Reconstruire la vérification côté serveur

La correction n’ajoute pas de complexité disproportionnée : elle déplace simplement le contrôle. Le fichier téléversé doit être analysé côté PHP au moment de la soumission, avec un identifiant de vérification généré et stocké côté serveur, jamais fourni ni modifiable par le client.

function cabinet_verifier_upload_identite( $request ) {
    $fichiers = $request->get_file_params();
    if ( empty( $fichiers['piece_identite'] ) ) {
        return new WP_Error( 'identite_absente', 'Aucune pièce jointe reçue.', array( 'status' => 400 ) );
    }

    $type = wp_check_filetype( $fichiers['piece_identite']['name'] );
    if ( ! in_array( $type['ext'], array( 'jpg', 'jpeg', 'png', 'pdf' ), true ) ) {
        return new WP_Error( 'format_invalide', 'Format de document non accepté.', array( 'status' => 400 ) );
    }

    // Le jeton de vérification est généré ici, jamais transmis par le client.
    $jeton = wp_generate_password( 32, false );
    set_transient( 'rdv_identite_' . $jeton, true, HOUR_IN_SECONDS );

    return array( 'jeton_verification' => $jeton );
}

Ce jeton, à usage unique et à durée de vie limitée, devient la seule preuve acceptée à l’étape suivante du formulaire. Le champ caché disparaît du modèle de confiance : il peut rester dans l’interface pour l’ergonomie, mais plus aucun traitement serveur ne s’y fie.

Ce que la fonction current_user_can ne résout pas ici

Il est tentant de penser qu’une vérification de capacité règle ce genre de problème. Ce n’est pas le cas dans ce contexte précis : le visiteur qui prend rendez-vous n’est généralement pas authentifié, donc current_user_can n’entre pas en jeu. La discipline à appliquer relève plutôt de la validation systématique de toute donnée transmise par un client non authentifié, y compris et surtout celles qui semblent techniques ou internes.

Règle maison retenue après cet audit : toute variable qui conditionne une décision métier doit pouvoir survivre à un client qui ment. Si elle ne le peut pas, elle ne doit jamais quitter le serveur.

Étendre le contrôle à d’autres formulaires sensibles

Le cabinet exploitait deux autres formulaires construits sur le même schéma technique : une demande de procuration et un dépôt de dossier de succession. Les trois partageaient la même faiblesse, preuve qu’un défaut de conception se propage silencieusement dès lors qu’un composant est dupliqué sans revue.

FormulaireContrôle avant correctifContrôle après correctif
Prise de rendez-vousChamp caché côté clientJeton serveur à usage unique
Demande de procurationChamp caché côté clientJeton serveur à usage unique
Dépôt de dossier de successionAucun contrôle expliciteValidation de type et jeton serveur

Notre verdict

Un formulaire qui touche à une exigence légale ou contractuelle de vérification d’identité ne peut jamais déléguer cette vérification au navigateur, même partiellement. La règle est simple à énoncer et pourtant régulièrement oubliée sous la pression du calendrier : toute logique de confiance doit se rejouer intégralement côté serveur, sans exception pour les champs qui paraissent techniques. Ici, corriger les trois formulaires a demandé moins d’une journée ; les repérer en a demandé beaucoup plus.

Partager :

À propos de l'auteur

Clément Hadrot

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

Voir tous ses articles

Dans la même veine

À lire aussi