6 min de lecture

Masquer les PII dans les logs : e‑mails, tokens et identifiants

Redigez les données personnelles dans vos logs : patrons pratiques pour masquer e‑mails, tokens et identifiants afin de déboguer sans exposer les données utilisateur.

Masquer les PII dans les logs : e‑mails, tokens et identifiants

Qu'est-ce qui compte comme PII dans les logs (et pourquoi ça réapparaît)

PII (informations personnellement identifiables) désigne toute donnée qui permet d'identifier une personne seule ou qui devient identifiable lorsqu'on la croise avec d'autres informations. Dans les logs et l'analytics, il s'agit en général des adresses e‑mail, numéros de téléphone, noms, adresses postales ou de facturation, et adresses IP. Cela inclut aussi des identifiants « techniques » qui, en pratique, pointent souvent vers une personne : user IDs, device IDs, advertising IDs, session IDs, cookies et localisation précise.

La PII apparaît parce que la journalisation est souvent ajoutée dans l'urgence, surtout lorsqu'un système est en panne. Le raccourci courant est « loggez l'objet entier » et nettoyez plus tard. Cet « objet entier » contient souvent des champs que vous n'aviez pas l'intention de stocker.

Les points d'entrée typiques sont les corps de requêtes (inscription, réinitialisation de mot de passe, messages au support), les en‑têtes (tokens Authorization, cookies, et parfois l'email dans des en‑têtes personnalisés), les objets d'erreur (qui peuvent inclure la requête originale ou des données utilisateur sérialisées), les query strings (paramètres de tracking, e‑mails collés dans les URLs) et les payloads de SDK tiers qui capturent automatiquement des infos device/network.

Les événements analytics présentent les mêmes risques. Les équipes copient des champs depuis les logs serveur vers des événements comme user_email, session ou des propriétés « debug » pour faciliter les graphiques. Ces événements se propagent ensuite vers plusieurs fournisseurs et dashboards, étendant le périmètre d'une seule erreur.

La redaction est meilleure que le simple « faites attention » car le mode de défaillance est prévisible : un nouvel endpoint est déployé, quelqu'un ajoute un log de debug, ou une bibliothèque change sa sérialisation. Considérez la redaction comme une couche de sécurité par défaut, pas comme une préférence développeur.

Décidez de ce que vous avez réellement besoin de garder pour le debug

Avant de tout masquer, clarifiez l'objectif de vos logs. La plupart des équipes collectent bien plus que ce qu'elles utilisent, et ces détails superflus sont là où se glissent e‑mails, tokens et en‑têtes bruts.

Notez les questions précises auxquelles vous attendez que les logs répondent lors d'un incident. Si une ligne de log n'aide pas à répondre à l'une de ces questions, c'est un bon candidat à supprimer ou à réduire.

La plupart des besoins de debug se résument à quelques éléments : ce qui a échoué et quand, quel endpoint/version a été appelé, si l'échec est survenu avant ou après l'auth, si une dépendance a causé l'erreur, et si plusieurs échecs appartiennent à la même requête ou session.

À partir de là, définissez une forme minimale « utile » de log applicable partout : timestamp, un request ID (ou trace ID), nom de la route, code de statut et un code d'erreur interne. Ajoutez un contexte contrôlé et limité comme des feature flags ou un nom de composant court. Évitez de dumper des objets entiers.

Séparez les besoins du support utilisateur de ceux de l'ingénierie. Le support a souvent besoin de retrouver un utilisateur et d'évaluer l'impact, mais ça ne nécessite pas de stocker l'e‑mail de l'utilisateur dans chaque événement. Une approche plus propre consiste à stocker une clé utilisateur interne stable dans les logs et à réaliser la recherche utilisateur dans un système d'administration sécurisé.

Certaines choses ne doivent jamais être loggées, même « temporairement » : mots de passe et codes à usage unique, tokens et clés API complets, cookies de session, en‑têtes Authorization bruts, et corps de requête/réponse complets.

Exemple : si des utilisateurs rapportent « échec de connexion », vous pouvez logger request_id=abc123, route=/login, status=401, error=AUTH_INVALID_TOKEN, auth_stage=post-parse. C'est suffisant pour débugger sans enregistrer le token.

Schémas de masquage pour les adresses e‑mail

Les adresses e‑mail apparaissent dans les logs parce qu'elles sont faciles à capturer : formulaires d'inscription, réinitialisations, invitations, tickets de support et erreurs « utilisateur introuvable ». Pour redacter les PII sans perdre de valeur pour le debug, conservez juste assez pour repérer des tendances (par exemple le domaine) sans exposer l'adresse complète.

Par défaut, il est sûr de conserver le domaine et un petit indice de la partie locale, par exemple j***@example.com ou jo***@example.com. C'est généralement suffisant pour remarquer « les échecs se concentrent sur example.com » sans révéler des identités.

L'adressage demande aussi de la prudence. [email protected] et [email protected] sont souvent la même boîte. Si vous masquez naïvement, vous pouvez traiter une seule personne comme plusieurs utilisateurs. Normalisez avant de masquer : mettez l'adresse en minuscules, supprimez le +tag, puis appliquez le masque.

Si vous avez besoin d'une corrélation stable entre événements, préférez un hash à clé plutôt qu'une révélation partielle : hash(email) + "@" + domain. Utilisez un secret d'application (un pepper) pour que le hash ne soit pas devinable à partir d'une liste d'emails courants. Ne loggez jamais l'email brut à côté du hash.

Le texte libre est la plus grande source de fuite : messages d'exception, prints de debug et copies de corps de requêtes. Ajoutez une étape de scan‑remplacement à votre logger pour que les e‑mails soient nettoyés même lorsqu'ils apparaissent au milieu de phrases.

Options courantes et utiles :

  • Domaine + 1–2 caractères locaux : ma***@gmail.com
  • Domaine + longueur de la partie locale : [email protected]
  • Hash à clé + domaine : a9f3c1…@example.com
  • Remplacement complet lorsque le risque est élevé : [REDACTED_EMAIL]

Quoi que vous choisissiez, appliquez‑le en un seul endroit (un helper de logging partagé) pour garantir la cohérence entre services et analytics.

Schémas de masquage pour tokens, clés API et IDs de session

Les secrets fuient parce qu'ils se trouvent dans des endroits « banals » que les ingénieurs loggent sans y penser : l'en‑tête Authorization, les cookies, les query strings, et les champs JSON comme token, apiKey, sessionId ou csrf. La règle est simple : ne loggez jamais de secrets bruts, même en cas d'erreur.

Les différents types de secrets nécessitent des traitements différents. Les clés API sont généralement longues et doivent être traitées comme des mots de passe. Les JWT peuvent contenir des claims lisibles, donc les logger peut divulguer des e‑mails ou des user IDs. Les session IDs et tokens CSRF peuvent être de courte durée, mais ils permettent tout de même le détournement de session.

Un schéma pratique consiste à garder juste assez pour corréler des événements :

  • Préfixe + suffixe : conservez les 4 premiers et les 4 derniers caractères
  • Longueur seulement : logger len=32 quand la corrélation n'est pas nécessaire
  • Étiquettes de type : kind=jwt ou kind=api_key pour faciliter le parsing/debug
  • Hash stable : un hash unidirectionnel quand vous avez besoin d'un rapprochement cohérent

Rendez votre redactor tolérant aux chaînes réelles. Les secrets apparaissent comme Bearer <token>, mais aussi à l'intérieur de messages concaténés, sans espaces, ou avec des casse étranges. Redactez par motif, pas par « joli format ».

Voici un exemple simple avant/après :

BEFORE  error="upstream 401" Authorization="Bearer eyJhbGciOi..." cookie="sid=s%3A0f1a9..." query="?api_key=sk_live_ABC123..."
AFTER   error="upstream 401" Authorization="Bearer eyJh...9Q2w" cookie="sid=\u003credacted len=48\u003e" query="?api_key=sk_l...8xKQ"

Gérer les user IDs, device IDs et autres identifiants

Repérer rapidement les fuites de PII
Nous identifierons où e‑mails, jetons et identifiants fuient dans vos logs et événements.

Tous les IDs ne sont pas inoffensifs. Si un identifiant peut être lié à une personne (directement ou en le croisant avec d'autres données), considérez‑le comme une donnée personnelle. Cela inclut de nombreux champs « internes » comme user_id, account_id, device_id, ip_address et des IDs de cookie « anonymes » s'ils persistent dans le temps.

Une règle utile : si vous pouvez l'utiliser pour retrouver une personne dans votre base, supposez que c'est sensible.

Préférez des IDs stables et non réversibles pour le debug

Vous devez tout de même pouvoir relier des événements lors d'un incident. Le schéma le plus sûr est une représentation stable mais non réversible, comme un hash salé. Vous obtenez une corrélation répétable sans exposer la valeur originale.

Par exemple, au lieu de logger user_id=483920, loggez user_key=hash(tenant_salt + user_id). Gardez le salt hors des logs, faites‑le tourner si nécessaire, et utilisez des salts séparés par environnement.

Pour que les logs restent utiles, incluez des champs de corrélation qui ne sont pas liés à une personne : request_id pour une unique requête, trace_id pour suivre un appel entre services, un session_key court qui expire rapidement, et tenant_id quand il identifie une organisation plutôt qu'un individu.

Les applications multi‑tenants demandent une attention supplémentaire. Un tenant_id est souvent sûr s'il représente une entreprise ou un espace de travail, pas un utilisateur unique. Ne hashez pas les user IDs globalement entre tous les tenants. Utilisez hash(tenant_id + user_id) pour empêcher de croiser les identifiants entre tenants.

Logs structurés vs non structurés (et comment les redacter)

Les logs non structurés sont l'endroit où la PII s'infiltre le plus souvent. Un rapide console.log(user) ou une erreur qui inclut les en‑têtes de requête peut dumper e‑mails, tokens et IDs dans une seule ligne désordonnée. Une fois envoyée à un outil de logs, c'est difficile à nettoyer.

La journalisation structurée (généralement JSON) rend la redaction prévisible. Au lieu d'essayer des regex sur une ligne entière, vous pouvez cibler des champs spécifiques comme user.email, auth.token ou request.headers.authorization.

Redactez d'abord au niveau des champs, puis utilisez la regex comme filet de sécurité pour le texte libre. La redaction uniquement par regex sur des lignes complètes rate des cas et peut aussi trop masquer, ce qui complique le debug.

Une approche pratique : loggez des métadonnées stables (endpoint, statut, feature flag, code d'erreur court), gardez la « forme » sans le contenu (longueur du token, domaine d'email, 4 derniers caractères), et séparez le texte libre en message plus context structuré. Ajoutez ensuite une étape finale de nettoyage pour toute chaîne restant de type email ou token.

Facilitez cela avec une seule utilité redact() utilisée partout (logging, reporting d'erreurs, analytics). Si différentes équipes implémentent leurs propres règles, vous passerez à côté de quelque chose.

export function redact(value) {
  if (value == null) return value;
  const s = String(value);

  // emails
  const email = s.replace(/[A-Z0-9._%+-]+@([A-Z0-9.-]+\.[A-Z]{2,})/gi, "\u003credacted:@$1\u003e");

  // bearer tokens / api keys (best-effort)
  return email.replace(/\b(Bearer\s+)?[A-Za-z0-9-_]{20,}\b/g, "\u003credacted:token\u003e");
}

Étape par étape : ajouter la redaction à votre pipeline de logs

La redaction fonctionne mieux lorsqu'elle est automatique. L'objectif est de supprimer les valeurs sensibles avant que quoi que ce soit ne quitte l'app en cours d'exécution, ainsi vous ne dépendez pas d'un paramètre fournisseur ou d'un job de nettoyage ultérieur.

Commencez par lister tous les endroits où des logs et événements sont créés. Les équipes se souviennent du serveur API, puis oublient l'worker, le reverse proxy, le client mobile ou le reporteur d'erreurs navigateur.

Ensuite, définissez une « carte de redaction » : noms de champs que vous ne voulez jamais expédier (comme email, authorization, cookie, set-cookie, password, token) plus des règles basées sur des motifs pour le texte libre.

Ajoutez ensuite une couche de redaction juste avant l'export, idéalement dans une fonction partagée pour qu'il soit difficile de la contourner. Pilotez le déploiement : ajoutez des tests, déployez progressivement et vérifiez que le debug fonctionne toujours.

Prouvez‑le avec des tests, pas de l'espoir

Utilisez des fixtures désordonnées : une requête avec Authorization: Bearer ..., un corps JSON contenant email, et une URL incluant un token de reset. Vos tests doivent confirmer que les éléments sensibles sont remplacés tandis que le contexte restant explique toujours ce qui s'est passé.

Redacter les PII dans les événements analytics sans perdre d'information

Corrections rapides et vérifiées
La plupart des projets FixMyMess se terminent en 48–72 heures après l'audit gratuit.

L'analytics fuit les mêmes types de données que les logs : URLs, referrers, valeurs de formulaires et messages d'erreur. La différence est leur destination. Les données analytics sont copiées vers plus d'endroits et souvent conservées plus longtemps.

Un bon par défaut est : envoyez moins, mais envoyez‑le de manière cohérente. Remplacez les propriétés utilisateur par un ID pseudonyme, puis n'ajoutez que des attributs grossiers réellement utilisés (niveau d'abonnement, version de l'app, pays, type d'appareil). Vous pouvez répondre aux questions produit sans expédier des identifiants bruts.

Les schémas sont connus : évitez e‑mail/phone/nom complet comme identifiants, résumez les chaînes sensibles (présent/non présent, longueur ou catégorie), supprimez query strings et fragments des URLs, et pour les erreurs envoyez un code et une catégorie plutôt que le payload complet qui peut contenir des saisies utilisateur. Si vous devez corréler, hashez avec un salt secret et n'envoyez jamais la valeur brute.

Les URLs brutes sont une fuite fréquente. Les liens de reset peuvent contenir token=..., les liens d'invitation peuvent inclure un e‑mail dans la query string, et ces valeurs se retrouvent capturées comme propriétés de page‑view.

Un événement d'inscription n'a généralement pas besoin de [email protected]. Il a typiquement besoin de signup_method=email, éventuellement email_domain=company.com, et d'un indicateur de succès de confirmation.

Erreurs courantes qui fuient encore des PII

La plupart des fuites ne viennent pas d'une règle de redaction « mauvaise ». Elles surviennent parce que quelqu'un a eu besoin de plus de détail pendant un incident, ou parce qu'un chemin de code logge différemment des autres.

Mode incident qui ne se désactive jamais

Un échec classique est d'activer la journalisation complète des requêtes pendant une panne (bodies, en‑têtes, query strings), de corriger le bug et d'oublier d'enlever la journalisation supplémentaire. Des semaines plus tard, les logs contiennent des mots de passe dans les payloads, des bearer tokens dans les en‑têtes et des e‑mails dans les query params.

La redaction aide, mais elle ne peut pas vous sauver si vous continuez à collecter bien plus que nécessaire.

Redaction trop étroite ou incohérente

Masquer uniquement un champ email n'est pas suffisant. La PII apparaît sous différentes clefs et formes imbriquées, et fuit depuis des endroits qu'on oublie : workers, jobs asynchrones, journalisation côté client et reporting d'erreurs.

Pour une promesse de bout en bout, les mêmes règles doivent s'exécuter partout où des logs sont produits, et elles doivent être testées avec des entrées réelles et désordonnées.

Liste de vérification rapide avant de livrer

Audit de code gratuit pour la journalisation
Envoyez-nous votre dépôt : nous cartographierons les chemins de journalisation à risque avant votre prochain déploiement.

Faites une passe finale avec un objectif simple : garder assez de détail pour débugger quoi et où, sans stocker de secrets ni d'identifiants directs.

  • Cherchez les pires coupables : mots de passe, en‑têtes Authorization, cookies et tokens complets. Si vous avez besoin de prouver l'existence d'un token, loggez seulement un fingerprint court (6 premiers et 4 derniers) ou un hash unidirectionnel.
  • Assurez‑vous que les adresses e‑mail et numéros de téléphone sont masqués partout, y compris dans le texte libre et les traces de pile.
  • Remplacez les identifiants utilisateur par des IDs anonymes (ou hashez‑les avec un salt stable) pour suivre un parcours sans exposer la valeur initiale.
  • Vérifiez les événements analytics pour des identifiants bruts et des URLs complètes. Les query strings contiennent souvent des tokens de reset, codes d'invitation ou indices de session.
  • Ajoutez des tests automatisés qui envoient des payloads courants (signup, login, reset de mot de passe, échecs de webhook) dans votre logger et assertent que la sortie ne contient pas de secrets.

Un contrôle rapide : déclenchez une connexion échouée en staging, puis regardez la ligne de log exacte. Si elle contient un e‑mail, un cookie ou un en‑tête auth, vous n'avez pas fini.

Prochaines étapes : vérifiez ce qui fuit et corrigez vite

Commencez par la preuve. Scannez les logs récents et les payloads analytics pour voir ce qui apparaît réellement : motifs d'email, chaînes ressemblant à des tokens, noms complets, adresses, IPs et dumps de debug devenus permanents.

Quand vous voyez des répétitions, traitez‑les comme un petit backlog. Corrigez d'abord les sources les plus à risque (auth, reset de mot de passe, inscription, facturation, formulaires de support), puis élargissez. Empêchez les nouvelles fuites avant de nettoyer les anciennes.

Si vous hérité d'une base de code générée par IA, ce travail avance souvent vite car les points de fuite se répètent dans les fichiers (dumps complets des requêtes, en‑têtes, cookies, variables d'environnement). Corrigez une fois à la frontière de logging et vous éliminez toute une classe d'erreurs.

Si vous avez besoin d'aide extérieure, FixMyMess (fixmymess.ai) se concentre sur le diagnostic et la réparation d'apps générées par IA, y compris le renforcement de la journalisation, la correction des flux d'auth cassés et le durcissement de la sécurité pour rendre les prototypes sûrs en production.",