Nous developpons votre site vitrine SPA À PARTIR DE 300€. Oui, c'est dingue. Web SPA À PARTIR DE 300€ — Oui, c'est dingue. Parlons-en →

Comment anonymiser des données personnelles et pouvoir les récupérer au besoin

Comment anonymiser des données personnelles et pouvoir les récupérer au besoin

Presque toute application sérieuse finit par envoyer du texte à l'extérieur : à un modèle d'IA, à un service de traduction, à un système de logs. Et ce texte ne part presque jamais propre. Cet article explique comment monter une couche qui remplace les données personnelles avant qu'elles ne sortent et les restitue à la réception de la réponse, pourquoi quatre expressions régulières ne suffisent pas, et ce qu'il faut pour que l'opération soit réversible sans ouvrir une nouvelle faille. Le code est en Laravel, l'une de nos spécialités chez AndorraDev, mais le problème est architectural et se règle de la même façon dans n'importe quel langage.

Envoyer un texte à un modèle, c'est lui envoyer tout ce qu'il contient

Quand votre application transmet un document à un modèle de langage, elle ne lui transmet pas "le contenu". Elle lui transmet la pièce d'identité, l'IBAN, le téléphone et le nom du client qui s'y trouvent. Et cela vaut aussi pour un journal applicatif, un service de traduction ou n'importe quel tiers.

Jusqu'à récemment, il n'y avait que deux réponses : ne rien envoyer, et se priver de la fonctionnalité, ou tout envoyer et faire confiance. Il en existe une troisième, et c'est celle que nous utilisons : substituer les données avant qu'elles ne sortent et les restituer à la réception de la réponse.

Notre client Juan Pérez, DNI 12345678Z, demande le virement vers ES9121000418450200051332

sort sous la forme

Notre client Juan «AP_1», DNI «DNI_1», demande le virement vers «IBAN_1»

Le modèle travaille sur cette version, répond en réutilisant les mêmes marqueurs, et au retour la substitution est défaite. Le fournisseur ne voit jamais la donnée réelle et vous ne perdez rien.

Une expression régulière ne distingue pas un DNI de huit chiffres quelconques

C'est là que presque toutes les solutions maison échouent. Chercher huit chiffres suivis d'une lettre trouve le DNI, et trouve aussi n'importe quelle référence interne ayant cette forme.

La différence entre un vrai détecteur et une expression régulière, c'est de valider la clé de contrôle. Un DNI porte une lettre calculée à partir du reste de la division du nombre par 23. Un IBAN se valide en modulo 97 selon la norme ISO 13616. Une carte passe par Luhn et par son préfixe d'émetteur.

Avec cette vérification, 12345678A n'est pas touché, parce que la lettre ne correspond pas. Et 12345678Z l'est. Cette distinction fait toute la différence entre un outil utilisable et un outil qui remplit le texte de faux marqueurs jusqu'à ce que le modèle ne comprenne plus rien.

Les noms n'ont pas de clé de contrôle

Les identifiants se valident. Les noms de personne, non, et il faut là une autre stratégie.

Celle qui marche combine deux signaux. Le premier, ce sont les titres de civilité : "D.", "Dña.", "Sr.", "Dr." introduisent une personne sans ambiguïté. Le second, ce sont les dictionnaires de prénoms et de noms de famille, qui pour l'espagnol proviennent du recensement de l'INE : plus de 16 000 prénoms et 24 000 noms de famille de fréquence suffisante.

Une suite de deux mots ou plus commençant par une majuscule ne compte comme personne que si son premier mot figure dans le dictionnaire de prénoms. Cela écarte "Tribunal Supremo" et "Audiencia Provincial" sans avoir besoin de les lister.

Et vient ensuite le détail que je préfère dans tout cela. En espagnol, certains prénoms sont aussi des noms communs, surtout ceux de tradition mariale : Luz, Milagros, Consuelo, Rosario. Ils figurent dans une liste à part, et la règle est qu'ils ne comptent que si un nom de famille est également confirmé :

La luz del sol. Luz Martínez vino. Luego Luz habló.
   ↓
La luz del sol. «PER_1» «AP_1» vino. Luego «PER_1» habló.

Le "luz" en minuscule reste intact. "Luz Martínez" est tokenisé parce qu'il y a un nom de famille. Et le "Luz" isolé de la fin est récupéré ensuite, car une fois que vous savez que dans ce texte Luz est une personne, vous pouvez en balayer les mentions isolées.

Andie recommande

Les noms sont substitués mot à mot, pas par personne. Cela paraît un détail mineur et ce n'en est pas un : deviner que "Juan Pérez" et "M. Pérez" sont le même individu est une décision d'identité, et se tromper là mélange les données de deux personnes différentes. En tokenisant chaque mot séparément, le texte est protégé sans que le système ait à parier sur qui est qui.

L'astuce que nous avons découverte avec le genre

Remplacer un nom par un identifiant paraît une opération neutre, et en anglais elle l'est presque. En français, non : le nom porte l'accord de tout ce qui suit, et en le retirant on emporte une information grammaticale dont le modèle avait besoin.

Si vous tokenisez le prénom, le modèle perd le genre, et en espagnol cela casse l'accord. "Juan Pérez ha sido informado" devient un texte où le modèle ne sait pas s'il doit écrire informado ou informada, et il tombe juste une fois sur deux.

La solution a été de laisser le prénom en clair et de tokeniser tout le reste :

app('laranon')->except('person')->newSession();

Le nom de famille sort, le DNI, l'IBAN, le téléphone et l'e-mail sortent. "Juan" reste. Et cette décision, qui ressemble à une concession, se révèle également correcte d'un point de vue juridique : un prénom isolé, sans nom de famille et sans aucun identifiant, a un pouvoir de réidentification très faible. Vous protégez ce qui identifie réellement.

Au passage, cela résout un autre problème. Les toponymes composés espagnols ("Santa María", "San Sebastián", "Palma de Mallorca") commencent par des mots présents dans le dictionnaire de prénoms, si bien qu'un détecteur fondé sur des dictionnaires les marque comme des personnes. C'est une limite inhérente à la méthode, pas au programme : sans contexte sémantique, "Santa María" et une dame prénommée María sont la même forme. En laissant les prénoms de côté, ce bruit diminue nettement.

Où vit la table de correspondances, et c'est là la décision importante

Pour pouvoir défaire la substitution, il faut conserver quelque part quel marqueur correspond à quelle valeur. Cette table est aussi sensible que les données d'origine, puisqu'elle est exactement la clé permettant de les reconstruire.

Il y a deux positions, et il vaut mieux choisir en connaissance de cause.

La conserver (en cache ou en base de données, chiffrée) permet qu'un même client reçoive toujours le même marqueur d'une conversation à l'autre. C'est confortable et c'est une donnée de plus à garder, avec sa politique de conservation et son droit à l'effacement.

Ne pas la conserver signifie que la table vit en mémoire pendant la requête et meurt avec elle. Chaque tour réanalyse le texte de zéro. C'est ce que nous faisons, et c'est la position du risque minimal : ce qui n'existe sur aucun disque ne peut pas fuiter.

Et il y a une troisième pièce que le règlement apprécie : pouvoir supprimer la table volontairement. Dès que vous la jetez, les marqueurs cessent d'être réversibles et ce qui était une pseudonymisation devient une anonymisation effective. C'est une opération en une étape, et c'est justement celle que vous voulez avoir sous la main quand quelqu'un exerce son droit à l'oubli.

Cela protège la frontière, pas l'entrepôt

Quand on met cela en place pour la première fois, on en retire souvent l'idée que les données sont désormais protégées, et c'est faux. Une couche d'anonymisation est un filtre de sortie, et tout ce qui ne franchit pas ce filtre reste exactement là où il était.

Substituer des données avant qu'elles ne sortent ne chiffre pas votre base de données. La conversation continue d'être enregistrée avec les valeurs réelles, parce que votre application en a besoin pour travailler. Ce qui est protégé, c'est le moment où le texte franchit la frontière : vers le modèle, vers le journal, vers le tiers.

Ce sont des problèmes distincts et ils appellent des réponses distinctes. Si votre obligation est que la donnée ne quitte pas votre infrastructure, ceci la remplit. Si c'est qu'elle n'existe pas en clair à l'intérieur, ce n'est pas le bon outil.

Comment vérifier que cela fonctionne vraiment

Un test qui vérifie que la fonction renvoie le texte substitué ne démontre rien, car le défaut intéressant n'est pas là. Il est dans la requête qui part sur le réseau.

La bonne façon de le tester est d'intercepter l'appel HTTP au fournisseur et d'affirmer sur le corps réel :

$this->assertStringNotContainsString('12345678Z', $envoye, 'le DNI est parti chez le fournisseur');
$this->assertStringNotContainsString('ES9121000418450200051332', $envoye, 'l\'IBAN est parti chez le fournisseur');
$this->assertStringNotContainsString('Pérez', $envoye, 'le nom de famille est parti chez le fournisseur');
$this->assertStringContainsString('Juan', $envoye, 'le prénom doit partir en clair');

Cette dernière ligne transforme le test en documentation : elle dit que laisser le prénom est intentionnel et non un oubli.

Et il y a une raison de tester ainsi. Les chemins qu'on oublie ne sont pas le chemin principal : ce sont le résumé de l'historique et la distillation des mémoires, qui envoient eux aussi la conversation au fournisseur et que personne ne regarde. Si vous vérifiez le corps HTTP, vous les couvrez tous par construction.

Ce qui existe sur le marché et en quoi cela diffère

La détection de données personnelles est depuis des années un problème de Python, et c'est là que se trouvent les outils matures. En PHP le paysage est plus pauvre, si bien que la décision ne porte pas seulement sur la bibliothèque à utiliser, mais sur l'opportunité de monter un service à part pour disposer de la bonne.

Microsoft Presidio est la référence libre. Il détecte les données personnelles en combinant des motifs et des reconnaisseurs fondés sur des modèles de langage, et il embarque un anonymiseur distinct. C'est l'option la plus complète si vous pouvez vous offrir un service Python à côté et la latence que chaque appel ajoute.

Google Cloud DLP et Amazon Comprehend font quelque chose de comparable en service géré, avec une ironie qu'il faut regarder en face : pour ne pas envoyer de données personnelles à un tiers, vous les envoyez à un autre tiers. Selon le contrat que vous avez avec chacun, cela peut suffire ou pas du tout.

Les bibliothèques d'expressions régulières sont ce qui finit par être utilisé en PHP la plupart du temps. Elles couvrent bien ce qui a un format, pièce d'identité, IBAN, téléphone, et s'arrêtent exactement là où le difficile commence, c'est-à-dire les noms propres.

Laranon, celui que nous maintenons, fait le pari de tout résoudre dans le processus, sans service externe et avec la table de correspondance sous votre contrôle, ce qui rend l'opération réversible. Si votre volume justifie de déployer Presidio, déployez Presidio. Si vous voulez qu'aucun texte ne sorte en clair de votre application sans ajouter d'infrastructure, c'est ce compromis-là.

Et si vous ne travaillez pas avec Laravel

Les critères se transposent, et ce sont ceux que vous devriez exiger de toute solution que vous évaluez :

  • Qu'elle valide, et pas seulement qu'elle cherche. Un détecteur sans clé de contrôle produit tant de faux positifs que vous finissez par le désactiver.
  • Qu'elle soit réversible et que vous décidiez où vit la table. Si l'outil conserve les correspondances dans son propre service, vous avez déplacé le problème, vous ne l'avez pas résolu.
  • Que vous puissiez exclure des types. Le cas du genre en espagnol, personne ne le prévoit avant de le rencontrer, et sans pouvoir dire "pas celui-là" vous êtes coincé.
  • Et que la vérification porte sur la requête qui sort. C'est le seul endroit où la vérité se voit.

Où est le code

Le paquet est publié sur Packagist, le code sur GitHub et la documentation du paquet recense toutes les options. Il est sous licence MIT, donc vous n'avez pas à croire sur parole qu'il anonymise bien : vous pouvez le lire. Il est en production sur Crowd Legal, où les dossiers que voit le copilote IA passent par là avant de sortir.

Si vous travaillez avec des données personnelles et que vous envisagez d'intégrer de l'IA à votre produit, c'est exactement le point où se décide la viabilité du projet. Nous en parlons dans intelligence artificielle et logiciel juridique, et si vous nous exposez votre cas, nous vous dirons ce qui est possible et ce qui ne l'est pas.

Écrit par
Edu Lazaro
Edu Lazaro
Founder & Lead Developer en AndorraDev

Développeur full-stack avec plus de 15 ans d'expérience en Laravel, React, Node.js et architectures cloud. J'aide les entreprises en Andorre à construire leur présence digitale.

Partner de diseño · ionospace.
Besoin d'aide? ×
Andie by AndorraDev
Assistant IA + équipe humaine
Assistant IA d'AndorraDev
Andie
Bonjour! Je suis Andie, l'assistant IA d'AndorraDev. Comment puis-je vous aider? Si vous souhaitez parler avec Edu, il suffit de le demander.
17:28