Suivi côté serveur sans fuite de données personnelles : le hashage SHA-256 expliqué

OpenAI n'accepte emails, téléphones et noms qu'en SHA-256. Règles de normalisation, extrait Node.js, et pourquoi un hash reste une donnée personnelle.

Publié le 13 min de lecture

Le suivi côté serveur envoie des identifiants client à une plateforme publicitaire pour qu’elle rapproche les conversions des utilisateurs. La Conversions API d’OpenAI n’accepte emails, numéros de téléphone, identifiants externes et noms que sous forme de hashes SHA-256 normalisés, et quelques champs comme l’adresse IP en clair. Le hashage empêche la valeur brute de sortir de votre infrastructure, mais un hash reste une donnée personnelle au sens du RGPD : consentement et contrat de sous-traitance restent obligatoires.

Ce qu’est l’advanced matching, et pourquoi il touche aux données personnelles

Un pixel navigateur identifie un visiteur par ce que le navigateur transporte : un identifiant de clic dans l’URL, un cookie first-party. Quand ce contexte manque (le script a été bloqué, le cookie a expiré, la commande a été confirmée par un webhook plutôt que par une page), la plateforme publicitaire a besoin d’un autre moyen de relier la conversion à un utilisateur qu’elle connaît. L’advanced matching est cet autre moyen : le marchand envoie des identifiants que la plateforme peut comparer à ses propres enregistrements.

Les identifiants en question sont ceux que votre boutique détient déjà sur chaque commande : email, numéro de téléphone, identifiant client, nom, adresse. Ce sont des données à caractère personnel. Le problème d’ingénierie est de les rendre utiles au rapprochement sans les expédier en clair à un tiers ; le problème juridique est de les traiter licitement même une fois hashées. Cet article couvre les deux, avec la Conversions API d’OpenAI comme cas concret. Le montage global à deux couches est décrit dans Pixel OAIQ ou Conversions API. Les citations de la documentation d’OpenAI et de Shopify sont traduites par nous ; celles du RGPD reprennent le texte officiel français.

Quels champs OpenAI accepte hashés, et lesquels en clair

L’objet user d’un événement Conversions API comporte deux sortes de champs. Chaque champ est facultatif ; plus vous en envoyez, meilleur est le rapprochement, et plus vous devez de diligence.

ChampFormeRègle de la référence
emails_sha256Liste de hashes SHA-256« hashes SHA-256 d’adresses email normalisées » : espaces retirés aux extrémités, minuscules
phone_numbers_sha256Liste de hashes SHA-256« 8 à 15 chiffres après avoir retiré un + initial, les zéros initiaux, les espaces, les parenthèses, les points et les tirets »
external_ids_sha256Liste de hashes SHA-256« hashes SHA-256 d’identifiants client stables et pseudonymes »
first_names_sha256Liste de hashes SHA-256« prénoms en minuscules après retrait des espaces et de la ponctuation ASCII »
last_names_sha256Liste de hashes SHA-256« noms en minuscules après retrait des espaces et de la ponctuation ASCII »
ip_addressClair« adresse IPv4 ou IPv6 valide »
user_agentClair« chaîne user agent non vide provenant du client »
countries, regions, cities, postal_codesClair, listesValeurs texte brutes, jusqu’à 3 valeurs chacune
obrefClair« référence navigateur opaque issue du cookie __obref du pixel »
android_advertising_idClair« identifiant publicitaire Google Android (GAID) brut au format UUID »

Un champ hashé est une liste, donc un client dont on connaît deux emails peut être rapproché par l’un ou l’autre. Chaque hash doit être la représentation hexadécimale minuscule du condensat SHA-256 : 64 caractères, 0-9a-f. Convrail valide cette forme avant tout appel réseau et rejette tout le reste.

Notez ce qui est absent : il n’existe aucun champ email, phone, first_name ni customer_id en clair. Une charge utile qui en contient un est au mieux malformée, au pire une fuite de données. La référence de la balise image énonce le même principe côté navigateur : « Ne mettez pas de données personnelles, de secrets, d’identifiants de session, d’identifiants client ou d’identifiants de commande dans un paramètre de requête ».

Les règles de normalisation, avec avant et après

Le hashage est déterministe : la même entrée donne toujours la même sortie, et une différence d’un caractère donne une sortie complètement différente. C’est ce qui rend le rapprochement possible, et c’est aussi pourquoi la normalisation compte plus que la fonction de hashage elle-même. Si vous hashez [email protected] et qu’OpenAI détient le hash de [email protected], les deux chaînes de 64 caractères n’ont rien en commun et le rapprochement échoue en silence.

Email

Valeur bruteNormaliséeSHA-256
[email protected][email protected]86e0b9e56c17cc4d12387e1949b85053fbe73bc3ce5a1188713a9d300cc6133d
[email protected][email protected]identique à la ligne précédente

Espaces retirés, minuscules, hash. Ne retirez pas les points ni les suffixes après + de la partie locale ; la référence ne le demande pas, et le faire produirait des hashes qu’OpenAI ne peut pas rapprocher.

Numéro de téléphone

Valeur bruteÉtapesNormaliséeSHA-256
+33 6 12 34 56 78retirer +, retirer les espaces336123456788a3e7886c9335e82e02299fa3e87b46e2de3b0c63d56003e30a5029394a47661
(+33) 6.12.34.56.78retirer les parenthèses, le +, les points33612345678identique à la ligne précédente
06 12 34 56 78retirer les espaces, retirer le zéro initial612345678d500e1b5b11d4a3049f3a6761ac1720f7d33ffc18160dc9ca1a68cbcd1948d9c

La troisième ligne est le piège. La règle documentée retire les zéros initiaux, donc un numéro français écrit au format national perd son zéro de tête et finit en 9 chiffres sans indicatif pays. C’est une chaîne valide de 8 à 15 chiffres, OpenAI l’acceptera, et elle ne correspondra jamais au 33612345678 du même client venu d’une autre source. La règle est une normalisation de format, pas une conversion au format international. Faites la conversion vous-même avant de l’appliquer : stockez les numéros en E.164 (+33612345678) dès la saisie, en résolvant l’indicatif grâce au pays de la commande, puis hashez le résultat de la règle appliquée à cette forme.

Noms

Valeur bruteNormaliséeSHA-256
Jean-Pierrejeanpierrefbcc8fa66b9a007dc3649ee989950d38f12ed91b9f18a3d215b04ff77546dca0
Doedoe799ef92a11af918e3fb741df42934f3b568ed2d93ac1df74f1b8d41a27932a6f
Zoëzoë2752b88686847fa5c86f47b94ce652b7b3f22a91c37617d451a4db9afa431450

Minuscules, retrait des espaces et de la ponctuation ASCII (le tiret de Jean-Pierre disparaît, l’apostrophe de O’Brien disparaît). Les lettres accentuées ne sont pas de la ponctuation ASCII et restent telles quelles ; ne translittérez pas ë en e. Hashez les octets UTF-8.

Identifiant externe

Tout identifiant stable et pseudonyme que votre système utilise déjà pour le client, comme un identifiant client Shopify. customer-7781 donne 91d8dedaef47ad2016875e1ecbb6f01c00bba531c0d4c3c5661ed3129ae381b0. Choisissez un format et ne le changez jamais, sinon chaque rapprochement passé se casse.

Une implémentation Node.js

Toute la normalisation tient en quelques lignes. L’étape téléphone suppose que le numéro est déjà en E.164, pour la raison donnée plus haut.

import { createHash } from 'node:crypto';

const sha256 = (value) => createHash('sha256').update(value, 'utf8').digest('hex');

export function hashEmail(raw) {
  const normalized = raw.trim().toLowerCase();
  return normalized ? sha256(normalized) : null;
}

export function hashPhone(e164) {
  // Règle de la référence Conversions API : retirer le + initial, les zéros initiaux,
  // les espaces, parenthèses, points et tirets ; conserver 8 à 15 chiffres.
  const digits = e164
    .replace(/^\+/, '')
    .replace(/[\s().-]/g, '')
    .replace(/^0+/, '');
  if (!/^\d{8,15}$/.test(digits)) return null;
  return sha256(digits);
}

export function hashName(raw) {
  const normalized = raw
    .toLowerCase()
    .replace(/\s/g, '')
    .replace(/[!-\/:-@\[-`{-~]/g, ''); // ponctuation ASCII seulement, les accents survivent
  return normalized ? sha256(normalized) : null;
}

export function buildUser(order) {
  const user = {
    emails_sha256: [hashEmail(order.email)].filter(Boolean),
    phone_numbers_sha256: [hashPhone(order.phoneE164)].filter(Boolean),
    external_ids_sha256: [sha256(String(order.customerId))],
    ip_address: order.clientIp,
    user_agent: order.userAgent,
    countries: [order.shippingCountry].filter(Boolean),
  };
  return Object.fromEntries(Object.entries(user).filter(([, v]) => v && v.length));
}

Renvoyer null pour une entrée vide ou malformée, puis la filtrer, compte : hasher une chaîne vide produit un condensat de 64 caractères parfaitement valide en apparence, qui correspond à tous les autres champs vides du monde. N’envoyez jamais le hash de rien.

Pourquoi hasher n’est pas anonymiser

C’est le point que les marchands ratent le plus souvent, en général parce qu’un fournisseur leur a dit « nous n’envoyons que des données hashées, donc le RGPD ne s’applique pas ». Il s’applique.

Le RGPD définit à l’article 4 point 1 les données à caractère personnel comme « toute information se rapportant à une personne physique identifiée ou identifiable ». L’article 4 point 5 définit la pseudonymisation comme « le traitement de données à caractère personnel de telle façon que celles-ci ne puissent plus être attribuées à une personne concernée précise sans avoir recours à des informations supplémentaires ». Le hash SHA-256 d’un email est exactement cela : seul, il ne désigne personne, mais quiconque détient l’email (vous, OpenAI si la personne est un utilisateur ChatGPT avec cette adresse, n’importe qui disposant d’une liste d’adresses à hasher et comparer) peut de nouveau l’attribuer à la personne. Le considérant 26 trace la ligne : les données pseudonymisées qui pourraient être attribuées à une personne physique par le recours à des informations supplémentaires « devraient être considérées comme des informations concernant une personne physique identifiable », alors que le règlement « ne s’applique, par conséquent, pas au traitement de telles informations anonymes ».

Conséquences pour une boutique qui envoie des identifiants hashés à une plateforme publicitaire :

  • Vous avez toujours besoin d’une base légale. Pour une mesure publicitaire impliquant un tiers, c’est en pratique le consentement recueilli par votre bandeau, ou une autre base que votre conseil a documentée. Le hashage change le profil de risque, pas la catégorie juridique. Convrail enregistre les événements des visiteurs qui ont refusé le suivi comme skipped_consent et ne les envoie jamais.
  • Vous devez toujours informer. Votre politique de confidentialité doit nommer le traitement (mesure des conversions avec OpenAI), les données concernées (email et téléphone hashés, adresse IP, user agent) et le destinataire.
  • Les droits de suppression et d’accès s’appliquent toujours. C’est pourquoi la mécanique de suppression décrite plus bas opère sur des hashes.

Là où le hashage aide, et il aide beaucoup : une charge utile qui fuit, un journal compromis, une requête interceptée ou une intégration mal configurée exposent des condensats, pas des adresses. Le rayon d’impact d’une erreur est bien plus petit. C’est une bonne raison de hasher ; ce n’est pas une raison de sauter le consentement.

Responsable du traitement, sous-traitant, et ce que chacun doit

L’article 4 point 7 définit le responsable du traitement comme celui qui « détermine les finalités et les moyens du traitement », et l’article 4 point 8 le sous-traitant comme celui qui « traite des données à caractère personnel pour le compte du responsable du traitement ». Quand vous connectez votre boutique à un connecteur de suivi :

  • Le marchand est le responsable du traitement. Vous décidez que les données de commande serviront à mesurer les conversions ChatGPT Ads, et vous décidez d’utiliser un outil donné pour le faire.
  • Le connecteur est un sous-traitant. Convrail reçoit les webhooks de commande, hashe les identifiants, met les événements en lots et les envoie à OpenAI sur vos instructions. L’article 28 paragraphe 3 exige que ce traitement soit « régi par un contrat ou un autre acte juridique », l’accord de traitement des données que vous devriez lire avant d’installer toute application de suivi, la nôtre comprise.
  • Le rôle d’OpenAI pour les données de rapprochement est défini dans ses propres conditions publicitaires, que vous acceptez en ouvrant un compte Ads. Lisez-les ; nous ne les reformulons pas ici.

Ce qu’un sous-traitant vous doit en pratique, et ce qu’il faut vérifier chez tout connecteur : hasher dès la réception pour que les valeurs en clair ne soient jamais stockées (Convrail hashe emails et numéros de téléphone à l’instant où un webhook arrive et ne conserve jamais la valeur en clair), chiffrer les identifiants de connexion au repos (AES-256-GCM dans le cas de Convrail), et supprimer sur demande.

Les demandes de suppression, appliquées par hash

Shopify rend le parcours de suppression concret avec trois webhooks obligatoires décrits dans sa documentation de conformité aux lois sur la vie privée :

WebhookQuand Shopify l’envoieCe que l’application doit faire
customers/data_requestQuand un client demande ses données au propriétaire de la boutiqueFournir les données du client au propriétaire de la boutique, à partir des identifiants de ressources de la charge utile
customers/redact« Si un client n’a pas passé de commande au cours des six derniers mois, Shopify envoie la charge utile 10 jours après la demande de suppression. Sinon, la demande est retenue jusqu’à ce que six mois se soient écoulés »Caviarder ou supprimer les données du client
shop/redact« 48 heures après qu’un propriétaire de boutique a désinstallé votre application »« effacer les données de cette boutique de votre base de données »

Pour chacun, l’application doit « accomplir l’action dans les 30 jours suivant la réception de la demande » et accuser réception avec un statut de la série 200.

Le piège pour un connecteur qui ne stocke jamais de valeurs en clair : la charge utile de customers/redact contient l’email et l’identifiant du client en clair, et le connecteur ne détient que des hashes. La réponse consiste à hasher les identifiants entrants avec exactement la même normalisation qu’à la capture et à supprimer chaque événement dont emails_sha256 ou external_ids_sha256 correspond. C’est une raison de plus pour que la normalisation soit déterministe et ne change jamais : une boutique qui passerait discrètement d’un email nettoyé de ses espaces à un email non nettoyé serait incapable de retrouver, donc de supprimer, les enregistrements créés avant le changement. Convrail applique la suppression de cette façon et purge toute la boutique sur shop/redact.

Comment le garde-fou de Convrail bloque les données personnelles en clair

Les règles de hashage ne valent que par leur application. Une refonte, un nouveau champ mappé depuis un webhook, un journal de débogage laissé actif : chacun de ces événements peut remettre un email en clair dans une charge utile des mois après la revue du code d’origine. Convrail place un garde-fou entre le constructeur de lots et le client HTTP, et il s’exécute sur chaque charge utile sortante sans exception.

Le garde-fou refuse d’envoyer un lot quand l’un des éléments suivants apparaît n’importe où dans la charge utile sérialisée :

  • une chaîne correspondant à un motif d’email (une partie locale, un @, un domaine) ;
  • une clé interdite comme email, phone ou first_name, quelle que soit sa position dans la structure ;
  • un champ hashé dont la valeur n’est pas exactement 64 caractères hexadécimaux minuscules.

Une charge utile refusée n’atteint jamais le réseau. Le garde-fou lui-même est couvert par un test automatisé qui tente de faire passer des données personnelles en clair et doit échouer ; si un changement futur affaiblit le garde-fou, le test échoue et le changement n’est pas livré.

C’est la propriété à exiger de tout outil de suivi côté serveur : pas « nous hashons », qui est une déclaration d’intention, mais « les données personnelles en clair ne peuvent pas sortir, et voici le mécanisme qui l’impose ».

Erreurs fréquentes

  • Hasher sans normaliser. Des majuscules, un espace parasite et un format téléphonique national produisent chacun un hash qu’OpenAI ne peut pas rapprocher. Le hash est bon ; l’entrée était fausse.
  • Hasher des chaînes vides. Un condensat de rien, valide en apparence, envoyé comme clé de rapprochement.
  • Appliquer la règle des zéros initiaux à des numéros nationaux. 06 12 34 56 78 devient 612345678 et ne correspondra jamais à 33612345678. Convertissez d’abord en E.164.
  • Translittérer les accents des noms. La règle retire la ponctuation ASCII seulement ; zoë reste zoë.
  • Saler. Un hash salé ne peut être rapproché par personne qui ignore le sel, ce qui annule l’objectif. L’advanced matching utilise du SHA-256 non salé par conception ; la protection de la vie privée vient du consentement et de la minimisation, pas du sel.
  • Considérer les données hashées comme hors du champ du RGPD. Ce sont des données à caractère personnel pseudonymisées. Les obligations de consentement, d’information et de suppression demeurent.
  • Mettre des identifiants dans des URL. La référence de la balise image interdit les données personnelles et les identifiants de commande dans les paramètres de requête ; il en va de même pour vos propres points de terminaison de relais et vos journaux.
  • Changer la normalisation plus tard. Chaque hash passé devient impossible à rapprocher et, pire, impossible à supprimer.

Et maintenant

Vérifiez ce que votre suivi actuel envoie en ouvrant une vraie charge utile et en y cherchant @ ; si vous préférez qu’un garde-fou le fasse sur chaque lot, le module de suivi des conversions de Convrail hashe dès la réception, bloque automatiquement les données personnelles en clair et applique les webhooks de suppression par hash.

Sources

Questions fréquentes

Hasher l'email d'un client le rend-il anonyme au sens du RGPD ?

Non. Le hash SHA-256 d'un email est une donnée pseudonymisée : quiconque détient l'email peut le relier à la personne, donc il reste une donnée à caractère personnel et le RGPD s'applique toujours, y compris l'exigence d'une base légale comme le consentement.

Quels champs client la Conversions API d'OpenAI accepte-t-elle en clair ?

L'adresse IP, le user agent, les pays, régions, villes et codes postaux, la référence navigateur obref et l'identifiant publicitaire Android. Emails, numéros de téléphone, identifiants externes, prénoms et noms ne sont acceptés qu'en hashes SHA-256.

Comment normaliser un numéro de téléphone avant de le hasher pour la Conversions API ?

Retirez le signe plus initial, les zéros initiaux, les espaces, les parenthèses, les points et les tirets, et conservez 8 à 15 chiffres. Stockez d'abord les numéros au format international pour qu'un numéro national et sa forme internationale donnent le même hash.

Qui est responsable du traitement quand un connecteur de suivi envoie des données hashées à OpenAI ?

Le marchand. La boutique décide pourquoi et comment les données client sont traitées pour la mesure publicitaire ; un connecteur comme Convrail les traite pour le compte du marchand sous contrat, ce qui correspond au rôle de sous-traitant défini à l'article 4 point 8 du RGPD.

Qu'arrive-t-il aux données hashées quand un client demande sa suppression ?

La demande est appliquée par identifiant hashé. Convrail purge chaque événement correspondant aux identifiants hashés du client à l'arrivée du webhook Shopify customers/redact, et un webhook shop/redact purge toute la boutique.

Mesurez le canal ChatGPT dès cette semaine

Gratuit pendant l'accès anticipé, le temps d'intégrer les premières boutiques. Laissez votre email et vous recevez le lien d'installation dès qu'une place s'ouvre.

Pas de newsletter. Un seul email avec le lien d'installation, rien d'autre.