Technical Deep-Dive

Anonymisation préservant le format : pourquoi XXXX casse votre pipeline IA

Remplacer 4532-1488-0343-6467 par XXXX-XXXX-XXXX-XXXX est la pire des rédactions : elle détruit silencieusement chaque réponse du LLM en aval. Voici quoi faire à la place.

ZTZeuslock Team··9 min
Comparaison côte à côte d'une carte bancaire caviardée en XXXX et d'une réécriture préservant le format qui garde visibles le préfixe Visa et les quatre derniers chiffres

Le problème avec XXXX

Voici un numéro de carte qui valide la clé de Luhn : 4532-1488-0343-6467. Redirigez-le naïvement vers ChatGPT en le caviardant :

Est-ce que XXXX-XXXX-XXXX-XXXX est un numéro de carte valide ?

Le modèle répondra à peu près « non, c'est un espace réservé, pas un vrai numéro ». Techniquement correct. Pratiquement inutile. La question que vous vouliez réellement poser, c'était « la carte que mon utilisateur vient de taper est-elle structurellement valide », et vous avez détruit chaque signal dont le modèle avait besoin pour y répondre.

Essayez maintenant la réécriture préservant le format :

Est-ce que 4532-1488-XXXX-6467 est un numéro de carte valide ?

Le modèle peut raisonner : le 4 initial indique Visa, 16 chiffres correspondent à la longueur Visa, les 4 derniers restent visibles pour l'affichage côté utilisateur, le milieu est manifestement masqué. Il répondra à la vraie question — oui, le motif est cohérent avec une vraie Visa, la somme de contrôle peut être recalculée, voici comment l'afficher au client.

Voilà l'écart entre la rédaction naïve et l'anonymisation préservant le format. La première est une case à cocher sécurité qui casse le produit. La seconde est ce que Zeuslock livre par défaut, parce que l'alternative — des utilisateurs qui désactivent le DLP parce qu'il rend leur assistant IA stupide — est strictement pire pour la sécurité que n'importe quel coût de fuite résiduelle.

La rédaction naïve est une destruction d'information

Si les développeurs ont le réflexe XXXX, c'est parce que ça paraît sûr. C'est le caviardage le plus fort : chaque octet du secret disparaît. Le problème, c'est que vous détruisez aussi les métadonnées. Trois éléments voyagent avec une valeur réelle, et vous ne voulez presque jamais perdre les trois :

  • Le format. Une chaîne de 16 chiffres avec tirets, c'est une carte. Un IBAN commence par deux lettres. Un JWT contient deux points. Un bearer token est en base64url. Le format est ce qui permet au LLM de savoir de quel objet on parle.
  • La sémantique à l'intérieur du format. Le 4 initial d'une Visa, le préfixe pays d'un IBAN, le préfixe AKIA d'une clé AWS long-terme, le kid d'un en-tête JWT — autant d'indices qui disent au modèle quelle bibliothèque, quel fournisseur, quelle juridiction est concerné.
  • Les terminaisons non secrètes. Les 4 derniers d'une carte, la longueur du local-part d'un e-mail, les chiffres de contrôle d'un IBAN — le contexte dont l'IA a besoin pour donner une réponse exploitable.

Supprimez les trois et vous ne demandez plus de l'aide à l'IA, vous lui demandez de deviner.

Cas 1 : cartes bancaires

Prenez 4532-1488-0343-6467. Luhn valide, 16 chiffres, le 4 initial identifie Visa, le BIN 453214 renvoie à un émetteur précis. Trois stratégies de caviardage, trois comportements LLM très différents.

StratégieChaîne envoyée au LLMCe que le LLM peut encore répondre
Original (sans DLP)4532-1488-0343-6467Tout. Et : violation PCI complète.
XXXX naïfXXXX-XXXX-XXXX-XXXXPresque rien. « C'est un espace réservé. »
Préservant le format4532-1488-XXXX-6467Visa. Motif à 16 chiffres. Modèle Luhn-valide. 4 derniers visibles. Émetteur déductible.

La ligne du milieu est celle que produisent la plupart des scripts DLP maison, et c'est aussi celle qui pousse l'utilisateur à coller le vrai numéro de carte dans son ChatGPT personnel pour obtenir une vraie réponse. La troisième ligne est celle qui garde à la fois l'équipe sécurité et l'utilisateur productifs.

Générer un remplaçant Luhn-valide et préservant le format en Python

Voici un petit générateur. À partir d'un vrai numéro, il garde les 6 premiers et les 4 derniers chiffres, et réécrit le milieu pour que la sortie continue de valider Luhn. L'original n'est ni journalisé ni persisté.

import secrets

def luhn_check_digit(digits: str) -> int:
    total = 0
    for i, ch in enumerate(reversed(digits)):
        d = int(ch)
        if i % 2 == 0:
            d *= 2
            if d > 9:
                d -= 9
        total += d
    return (10 - (total % 10)) % 10

def format_preserve_card(original: str) -> str:
    digits = ''.join(c for c in original if c.isdigit())
    if len(digits) != 16:
        raise ValueError('attendu : carte à 16 chiffres')
    bin6, last4 = digits[:6], digits[-4:]
    # 5 chiffres aléatoires au milieu + 1 chiffre de Luhn calculé
    middle = ''.join(str(secrets.randbelow(10)) for _ in range(5))
    candidate = bin6 + middle + '0' + last4
    check = luhn_check_digit(candidate[:-1] + last4[:-1])
    fake = bin6 + middle + str(check) + last4
    return f'{fake[:4]}-{fake[4:8]}-{fake[8:12]}-{fake[12:]}'

Le résultat est une Visa réaliste à 16 chiffres qu'aucun processeur ne facturera jamais, parce qu'elle n'est statistiquement pas attribuée. Le LLM la traite comme une carte, l'émetteur est correctement déduit, et l'utilisateur obtient une réponse utile.

Cas 2 : IBAN

Un IBAN comme FR76 3000 6000 0123 4567 8901 234 porte quatre couches de sens : pays (FR), chiffres de contrôle (76), code banque/guichet (3000 6000), numéro de compte, et une clé nationale. Le XXXX naïf détruit les quatre. Demandez ensuite au LLM « de quel pays vient ce compte ? » et vous obtenez un refus.

Réécriture préservant le format : FR76 XXXX XXXX XXXX XXXX XXXX 234. Le pays et la clé mod-97 sont conservés. Un modèle aval répond avec assurance « France », et un générateur peut reconstruire une somme de contrôle plausible si votre pipeline de tests en a besoin.

Arbitrage : garder FR76 fait fuiter une donnée géographique. Pour un workflow support client, c'est bien et probablement souhaité. Pour un pipeline RH interne qui traite des collaborateurs dans plusieurs juridictions, peut-être pas. La préservation du format est un choix de politique, pas un défaut universel. Zeuslock vous laisse régler ça par type de donnée et par pipeline.

Cas 3 : clés d'accès AWS

Une clé d'accès AWS long-terme ressemble à AKIAIOSFODNN7EXAMPLE. Le préfixe AKIA n'est pas aléatoire — il identifie le type (ASIA pour les jetons STS temporaires, AKIA pour IAM long-terme). La longueur est fixée à 20.

Caviarder en XXXX ne dit rien au LLM. Préserver le format en AKIA**************** lui dit « c'est une clé IAM long-terme, longueur 20, entropie secrète supprimée ». Le remplacement n'est pas une clé valide — AWS la rejettera instantanément — mais le modèle peut désormais répondre correctement « faites tourner la clé via aws iam create-access-key, puis révoquez l'ancienne » au lieu de « je ne peux pas aider avec des chaînes d'espace réservé ».

Même principe pour Stripe (sk_live_), OpenAI (sk-), GitHub (ghp_), Slack (xoxb-), GitLab (glpat-), npm (npm_). Le préfixe, c'est le type. Gardez le préfixe, tuez l'entropie.

Cas 4 : JWT

Un JWT a trois segments séparés par des points : header.payload.signature. L'intérêt d'un JWT, c'est précisément de pouvoir décoder l'en-tête pour découvrir l'algorithme sans vérifier la signature. Si un développeur colle un vrai JWT dans Claude en demandant « pourquoi mon jeton échoue à la validation », le modèle a besoin de la structure pour donner un conseil utile.

La rédaction naïve écrase le JWT en XXXX. Le modèle n'a plus rien à analyser. La version préservant le format conserve les points et les longueurs de segment :

eyJhbGciOiJIUzI1NiJ9.XXXXXXXXXXXXXXXX.XXXXXXXXXXXX

Le modèle voit trois segments, peut commenter l'en-tête visible (« alg: HS256, vous voulez probablement RS256 pour une vérification asymétrique »), et les octets de signature ne voyagent jamais. Le développeur obtient une vraie réponse ; le secret reste local.

Cas 5 : e-mails — le plus délicat

L'e-mail est le cas où la bonne réécriture dépend de ce que vous cherchez à protéger. Deux options raisonnables pour alice@acme.com :

RéécritureCe qui survitQuand c'est le bon choix
user@example.comForme générique uniquement.Le LLM doit savoir « c'est un e-mail ». L'identité et l'employeur sont tous deux sensibles. Choix par défaut.
XYxYx@acme.comDomaine préservé.Vous déboguez un problème de délivrabilité, un enregistrement SPF, une règle de routage interne. Le domaine est le contexte porteur.
alice@example.comLocal-part préservé.Presque jamais le bon choix. Le local-part est souvent l'identité.

La ligne du milieu mérite réflexion. Conserver le domaine fait fuiter l'employeur de l'utilisateur. Pour un outil de support client c'est acceptable — l'agent sait qu'il s'agit d'acme.com — mais pour un pipeline de tri de CV ça peut faire fuiter des caractéristiques protégées. Même règle que pour les IBAN : la préservation du format est une politique, pas un défaut.

Quand préserver le format est le mauvais choix

L'intérêt de préserver le format, c'est que le format porte une information sémantique utile et non secrète. Il existe des catégories où le format est le secret. Dans ces cas, anonymisez plus dur, ou bloquez.

  • Identifiants type NIR. Le NIR français encode sexe, année et mois de naissance, département et commune. « Préserver le format » d'un NIR, c'est faire fuiter de la démographie. La bonne approche est la rédaction totale ou le blocage. La CNIL a une position claire là-dessus pour les données de catégorie particulière sous RGPD.
  • Identifiants client internes. Si votre ID client est ACME-2024-018472 et que l'année encode la cohorte d'onboarding, le format fait fuiter de l'intelligence business. Traitez-le comme opaque.
  • IDs séquentiels dans un petit espace. Si vous n'avez que 5 000 collaborateurs et que l'ID employé est séquentiel, préserver le format réduit l'entropie du caviardage presque à zéro — le modèle (ou un observateur curieux) peut ré-identifier la personne via le contexte.
  • Tout ce pour quoi le régulateur a dit « ne conservez pas d'identifiants pseudonymes sans justification ». Si vous ne pouvez pas pointer une finalité de traitement légitime pour garder le préfixe, supprimez le préfixe.

L'arbitrage que Zeuslock a fait, et pourquoi

Les défauts de Zeuslock penchent du côté de la préservation de structure. La raison est une observation comportementale simple : quand le DLP rend l'assistant IA inutile, l'utilisateur n'abandonne pas l'assistant IA. Il abandonne le DLP. Il colle son prompt dans un ChatGPT personnel, sur un téléphone, ou sur le navigateur du café d'à côté. La fuite a quand même lieu, et vous avez perdu la piste d'audit.

Le plus difficile dans la construction d'un DLP pour LLM n'est pas la détection. C'est de rendre l'expérience protégée suffisamment bonne pour que l'utilisateur n'essaie pas activement de la contourner.

L'anonymisation préservant le format est la primitive technique qui rend cela possible. L'utilisateur continue d'obtenir des réponses utiles, les octets secrets restent sur le poste, et l'opérateur dispose d'une piste d'audit complète indiquant quel détecteur s'est déclenché et quelle réécriture a été appliquée. Par type de donnée, vous pouvez durcir n'importe quelle réécriture jusqu'au XXXX, ou escalader vers Bloquer — mais le défaut reste structuré-mais-faux, parce que c'est le défaut qui survit au contact des vrais utilisateurs.

À faire ensuite

  1. Auditez votre caviardage actuel. Si quelque part dans votre stack vous remplacez les valeurs détectées par [REDACTED], XXXX ou une chaîne vide avant qu'elles n'atteignent un LLM, vous dégradez la réponse pour aucun gain de sécurité par rapport à une réécriture structurée.
  2. Choisissez la politique de préservation par type de donnée. Cartes et JWT en bénéficient presque toujours. IBAN et e-mails sont des arbitrages. Les identifiants type NIR doivent en général être totalement caviardés.
  3. Montrez la réécriture à l'utilisateur avant l'envoi. L'aperçu pré-envoi de Zeuslock forme l'habitude plus vite que n'importe quel programme de sensibilisation — voir le guide opérateur sur la configuration des politiques de détection pour le déploiement recommandé Monitor → Anonymiser → Bloquer.

Protect your data from AI leaks

Try Zeuslock free — DLP for ChatGPT, Claude, Gemini and more.

Book a demo →