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

Extensions

Une extension santé : anonymiser les journaux avant tout envoi à un tiers

Un identifiant patient dans un message Sentry, et c'est tout un dossier RGPD qui s'ouvre. Filtrer les journaux avant leur départ n'est pas une option, c'est une architecture.

Par WordPress Développement • 6 juillet 2023 • 4 min de lecture • Aucun commentaire
Une extension santé : anonymiser les journaux avant tout envoi à un tiers

Trente-six mille dossiers patients transitent chaque mois par une extension de prise de rendez-vous en ligne pour un réseau de centres de soins. Chaque erreur applicative génère un message envoyé à Sentry pour le suivi technique, et chaque webhook manqué déclenche une notification vers un canal de support externe. Le problème arrive dès la première ligne de code non relue : un message d’erreur qui contient, sans y penser, le nom complet et le numéro de sécurité sociale partiel d’un patient.

Ce n’est pas un cas théorique. Les messages d’exception PHP incluent par défaut la trace complète des variables locales dans certains contextes de débogage, et un développeur pressé insère volontiers print_r( $rendez_vous ) dans un message de log « pour comprendre le bug plus tard ». Cet article décrit une architecture de filtrage systématique, appliquée avant tout envoi vers un service externe, sans traiter la question distincte — et tout aussi critique — de l’hébergement HDS lui-même.

Centraliser la liste des champs sensibles

La première règle est de ne jamais laisser chaque appel de log décider seul de ce qu’il doit filtrer. Une liste unique de clés sensibles, maintenue à un seul endroit, réduit le risque d’oubli :

final class WPM_Champs_Sensibles {
    public const LISTE = array(
        'nom', 'prenom', 'email', 'telephone',
        'numero_secu', 'date_naissance', 'adresse',
        'notes_praticien', 'motif_consultation',
    );
}

Cette classe devient la référence unique consultée par tout code qui prépare une charge utile à destination d’un service tiers, qu’il s’agisse de Sentry, d’un webhook Slack interne ou d’un export de diagnostic.

Un filtre de rédaction appliqué récursivement

Le filtre doit parcourir n’importe quelle structure de données, y compris des tableaux imbriqués représentant des objets de rendez-vous complexes, et remplacer chaque valeur sensible par un marqueur explicite plutôt que de simplement la supprimer, ce qui casserait la lecture du contexte par l’équipe technique.

L'essentiel à retenir : Tout log destiné à un service tiers doit passer par un filtre de rédaction ; La liste des champs sensibles doit être centralisée, jamais dupliquée par appel ; Un test automatisé doit garantir qu'aucun identifiant ne fuit après refactoring
function wpm_rediger_contexte( array $donnees ) : array {
    $sensibles = WPM_Champs_Sensibles::LISTE;

    array_walk_recursive( $donnees, function ( &$valeur, $cle ) use ( $sensibles ) {
        if ( in_array( $cle, $sensibles, true ) ) {
            $valeur = '[REDACTED]';
        }
    } );

    return $donnees;
}

add_filter( 'wpm_avant_envoi_sentry', 'wpm_rediger_contexte' );
add_filter( 'wpm_avant_envoi_webhook_support', 'wpm_rediger_contexte' );

Le choix d’un filtre WordPress plutôt que d’une fonction appelée manuellement à chaque endroit garantit qu’aucun nouveau point d’envoi ajouté plus tard par un autre développeur ne peut oublier l’étape de rédaction — à condition que le point d’envoi respecte, lui, la convention d’appeler ce filtre avant toute transmission.

Le cas particulier des traces d’exception

Une exception PHP capturée via un gestionnaire global expose potentiellement l’intégralité de la pile d’appel, variables locales comprises selon le niveau de détail configuré. La bonne pratique consiste à ne jamais transmettre l’objet exception brut, mais un résumé construit explicitement :

set_exception_handler( function ( \Throwable $e ) {
    $resume = array(
        'message' => $e->getMessage(),
        'fichier' => basename( $e->getFile() ),
        'ligne'   => $e->getLine(),
    );

    wpm_envoyer_vers_sentry( wpm_rediger_contexte( $resume ) );
} );

Tester la non-fuite, pas seulement l’espérer

  • Un test unitaire injecte un jeu de données factices contenant chaque champ sensible connu, puis vérifie que le résultat filtré ne contient plus aucune de ces valeurs.
  • Un test de non-régression tourne à chaque modification du filtre pour garantir qu’un renommage de clé (par exemple numero_secu devenu numero_secu_partiel) n’échappe pas silencieusement à la liste.
  • Une revue de code impose qu’aucun appel direct à l’API du service tiers ne contourne le filtre central.

La consigne donnée à l’équipe sur ce projet tient en une phrase : si un champ n’est pas explicitement listé comme sûr à exporter, il est traité comme sensible par défaut, jamais l’inverse.

En résumé

Anonymiser après coup, en croisant les doigts pour qu’aucun développeur n’oublie, ne suffit pas sur une extension santé. La seule architecture défendable devant un audit RGPD est celle où le filtrage est structurel : une liste de champs sensibles centralisée, un filtre appliqué systématiquement avant tout départ vers un service tiers, et des tests qui vérifient concrètement l’absence de fuite plutôt que de se contenter d’une relecture manuelle. Ce coût d’ingénierie, payé une fois, protège ensuite chaque nouvelle fonctionnalité ajoutée à l’extension.

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