Tes employés envoient des données client à ChatGPT — comment garantir qu'OpenAI ne les voit jamais
Un salarié colle un email avec le nom, le numéro de CB et l'IBAN du client dans ChatGPT pour gagner du temps. Sans contrôle, OpenAI a maintenant accès à ces données. PromptVault remplace automatiquement les données sensibles par des jetons avant l'envoi et restaure les vraies valeurs dans la réponse — sans casser la conversation. Conformité RGPD garantie.
Cet article décortique le projetPromptVaultLe problème : ChatGPT en entreprise
Vos équipes utilisent ChatGPT, Claude, Gemini. Tous les jours. Et tous les jours, dans les prompts, partent vers OpenAI :
- des emails clients,
- des IBAN,
- des numéros de carte bancaire,
- des dates de naissance,
- des noms complets.
Aucun DPO ne peut accepter ça. La réponse "interdisez ChatGPT" ne tient pas une semaine — les commerciaux trouveront un VPN, les développeurs colleront leur code dans un onglet privé.
La seule réponse défendable : masquer avant envoi, restaurer dans la réponse, sans que l'utilisateur change ses habitudes.
C'est l'approche de PromptVault. Le masquage est aujourd'hui câblé côté gateway et dans l'extension VS Code ; côté Chrome, c'est la restitution dans la réponse qui est en place. Voici comment fonctionne le pipeline.
En bref
Une extension navigateur (+ gateway entreprise) qui masque les PII avant l'envoi à ChatGPT/Claude (9 détecteurs regex) et restitue les vraies valeurs dans la réponse — la table de correspondance ne quitte jamais le poste. Ce que ça montre : maîtrise fine du DOM (MutationObserver sur un stream React sans le casser), RGPD by design, et un back-office .NET 10 / Blazor + Fusion.
Pipeline en 3 étapes
1. Détecter — regex pur
Le détecteur est volontairement boring : ~9 regex déterministes (IBAN, email, numéro de sécu, téléphone FR/INT, carte bancaire, date naissance, IP, montant). Pas de ML, pas de NER — un détecteur 100 % regex, auditable ligne par ligne. C'est moins "intelligent" qu'un modèle de reconnaissance d'entités, mais c'est déterministe, rapide, et explicable devant un DPO.
const REGEX_PATTERNS = [
{ type: 'SECURITE_SOCIALE', pattern: /[12]\s?\d{2}\s?\d{2}\s?\d{2}\s?\d{3}\s?\d{3}\s?\d{2}/g },
{ type: 'IBAN', pattern: /[A-Z]{2}\d{2}\s?[\dA-Z]{4}\s?(?:[\dA-Z]{4}\s?){2,7}[\dA-Z]{1,4}/g },
{ type: 'CARTE_BANCAIRE', pattern: /\b(?:4\d{3}|5[1-5]\d{2}|3[47]\d{2})\s?\d{4}\s?\d{4}\s?\d{4}\b/g },
{ type: 'EMAIL', pattern: /[\w.-]+@[\w.-]+\.\w{2,}/g },
{ type: 'TELEPHONE', pattern: /(?:\+33|0)\s*[1-9](?:[\s.-]*\d{2}){4}/g },
{ type: 'DATE_NAISSANCE', pattern: /\b(?:0[1-9]|[12]\d|3[01])\/(?:0[1-9]|1[0-2])\/(?:19|20)\d{2}\b/g },
{ type: 'MONTANT', pattern: /\d[\d\s,.]*(?:€|\$|£|EUR|USD|CHF)/g },
// …
];
Le masquage est câblé côté gateway (serveur d'entreprise) et dans l'extension VS Code,
qui partagent le même module shield/. La table de correspondance reste locale — c'est le
principe de base : ce que le serveur ne voit pas, il n'a pas à le sécuriser.
2. Anonymiser — réversible et stable
Le piège classique : on remplace les occurrences une par une, et les indices de fin de chaîne décalent au fur et à mesure. Solution : itérer à l'envers.
export function anonymize(text: string, config: ShieldConfig): AnonymizationResult {
const entities = detectEntities(text, config);
const mapping = new Map<string, { original: string; type: string }>();
const counter = new Map<string, number>();
let result = text;
// Itération inverse — les indices avant l'entité courante restent valides
for (let i = entities.length - 1; i >= 0; i--) {
const e = entities[i];
const count = (counter.get(e.type) ?? 0) + 1;
counter.set(e.type, count);
const placeholder = `[${e.type}_${count}]`;
mapping.set(placeholder, { original: e.text, type: e.type });
result = result.substring(0, e.start) + placeholder + result.substring(e.end);
}
return { anonymizedText: result, mapping, entitiesCount: entities.length, entityTypes: [...new Set(entities.map(e => e.type))] };
}
Trois subtilités qu'on ne voit qu'en production :
a. Tokens lisibles
Pas de hash, pas d'UUID. [EMAIL_1], [IBAN_1], [NAME_1]. ChatGPT comprend ce qu'il
manipule et écrit "envoyez votre relance à [EMAIL_1]" plutôt que d'inventer un email aléatoire.
Le modèle reste utile.
b. Numérotation par type
Marie Dupont devient [NAME_1], son mari Pierre Dupont devient [NAME_2].
Le LLM ne fusionne pas les deux personnes. L'identité relative est préservée.
c. Map kept local
La table placeholder → original ne quitte jamais le navigateur. Elle vit dans la
mémoire de l'extension le temps de la requête. C'est ce qui rend l'audit RGPD trivial :
il n'y a rien à auditer côté serveur, le serveur n'a jamais vu la donnée.
3. Restituer — MutationObserver sur ChatGPT
C'est l'étape qui m'a pris le plus de temps. ChatGPT streame sa réponse mot à mot via
React. On ne peut pas simplement faire innerHTML.replace() : on casserait l'hydration React.
Solution : MutationObserver + TreeWalker.
export function startDesubstitutionObserver(
responseElement: HTMLElement,
mapping: Map<string, { original: string; type: string }>
) {
const observer = new MutationObserver(() => {
for (const [placeholder, { original }] of mapping) {
const walker = document.createTreeWalker(responseElement, NodeFilter.SHOW_TEXT);
let node;
while ((node = walker.nextNode())) {
if (node.textContent?.includes(placeholder)) {
node.textContent = node.textContent.replaceAll(placeholder, original);
}
}
}
});
observer.observe(responseElement, { childList: true, subtree: true, characterData: true });
// Auto-disconnect après 60s — pas de leak si l'utilisateur change de conversation
setTimeout(() => observer.disconnect(), 60000);
}
Pourquoi TreeWalker plutôt qu'un simple innerHTML.replace() ?
| innerHTML.replace | TreeWalker SHOW_TEXT | |
|---|---|---|
| Casse React | Oui — re-render forcé | Non — mutation feuilles |
| Liens / images | Endommagés | Préservés |
| Code blocks ChatGPT | Décolorés | Intacts |
| Streaming partiel | Crée des flickers | Continu |
Le setTimeout(disconnect, 60_000) est important. ChatGPT garde la conversation montée
en DOM des heures. Sans déconnexion, on aurait 30 observers actifs en parallèle.
Le compteur de tokens, en bonus
Effet de bord intéressant : comme on intercepte chaque prompt, on peut le compter. L'idée du produit est de montrer à l'utilisateur combien de tokens il a envoyé, combien ça coûte, et combien on économiserait en routant via un autre provider en mode multi-provider.
Le projet est privé et encore en développement : il n'y a pas de tableau de bord DPO avec des chiffres réels en production. Mais le levier est clair — rendre visible un coût jusqu'ici invisible, c'est la moitié de la conversation.
Ce que ce projet m'a appris
-
Le DOM observer pattern est sous-utilisé. Tout le monde fait des extensions qui injectent du contenu en début de page. Très peu construisent une couche qui réagit au stream React d'un produit moderne sans le casser.
-
La conformité ne se vend pas sur des slides. Un compteur "N PII masquées sur ce prompt" affiché en direct fait comprendre le ROI bien plus vite que dix pages de PowerPoint.
-
Reversible > anonymous. Les outils existants soit anonymisent définitivement (et le LLM rend une réponse abstraite), soit ne masquent rien. La voie du milieu — masquer, laisser le LLM raisonner sur des placeholders, restituer dans la réponse — préserve à la fois l'usage et la conformité.
Stack & code
- Extension Chrome MV3 + VS Code extension (même
shield/partagé) - Preact 10 pour le sidepanel — léger, pas de virtual DOM coûteux
- IndexedDB pour les prompts sauvegardés
- Backend Blazor .NET 10 + Fusion côté entreprise (audit, marketplace de prompts, chaînes de prompts, ROI tracking) — ~34 modules métier, dont une quinzaine exploitent le pattern de compute Fusion complet
Important pour être honnête : le masquage est aujourd'hui câblé côté gateway et dans
l'extension VS Code. Dans l'extension Chrome, la restitution (le MutationObserver
ci-dessus) est branchée, mais le masquage en amont de l'envoi ne l'est pas encore — c'est
du code présent mais non monté dans le flux. Le produit est privé et en développement.
Le module shield/ reste volontairement compact et lisible en quelques minutes — c'est
précisément ce qui le rend défendable en audit sécurité : un détecteur regex qu'on relit
intégralement vaut mieux qu'un modèle opaque.
Plus de détails sur l'architecture et la stack sur la page projet PromptVault.
Florian Sola
Développeur Senior C#/.NET/GenAI — Lead Tech & Architecte logiciel · 9 ans d'expérience
La suite logique
Ce sujet ressemble à ce que vous devez livrer ? Parlons-en.
Je prends des sujets techniques critiques — du cadrage à la production, sans dette ni dépendance après le transfert. Le plus rapide pour voir si ça colle : 30 minutes en visio.
Réponse sous 24 h — souvent bien avant.