Une mutuelle envoie chaque année un email qui invite ses adhérents à vérifier leurs coordonnées. Le bouton mène à une CloudPage qui affiche l'adresse postale, le téléphone et l'email, avec un formulaire de modification. Le lien a été construit vite, quelques jours avant l'envoi, avec le numéro d'adhérent placé directement dans l'adresse.
Trois semaines plus tard, un adhérent écrit au délégué à la protection des données. Il a remarqué son numéro dans la barre d'adresse, l'a remplacé par un numéro voisin, et a vu s'afficher les coordonnées de quelqu'un d'autre. Les numéros sont séquentiels, n'importe qui pouvait parcourir le fichier.
La page fonctionnait parfaitement en recette, et rien dans son code n'était faux au sens technique. Elle lisait un paramètre, cherchait la ligne correspondante et l'affichait. Le défaut tient entièrement à la confiance accordée à ce paramètre, et il s'agit d'une violation de données personnelles, avec les obligations de documentation et de notification qui l'accompagnent.
Une URL ne reste pas dans le navigateur de la personne à qui vous l'avez envoyée. Elle passe dans l'historique, dans les emails transférés, dans les journaux des proxys d'entreprise, dans les outils d'analyse, et dans l'en-tête envoyé aux sites appelés par la page. Tout ce qu'elle contient doit être considéré comme connu de tiers.
Salesforce le dit sans détour : ne transmettez pas de SubscriberID, de Subscriber Key ni de Contact Key en clair, et n'incluez pas de données personnelles comme l'adresse email dans les liens.
| Façon de passer l'identifiant | Lisible par un tiers | Modifiable à la main | Verdict |
|---|---|---|---|
| Numéro en clair dans la requête | Oui | Oui | À proscrire |
| Encodage Base64 ou hexadécimal | Oui, se décode en une seconde | Oui | À proscrire, ce n'est pas un chiffrement |
CloudPagesURL | Non | Non, une chaîne altérée ne se déchiffre pas | Méthode recommandée depuis un email |
EncryptSymmetric avec clé gérée | Non | Non | Pour les liens construits hors d'un email |
⚠️ Base64 n'est pas du chiffrement : Salesforce cite expressément cet encodage et la conversion en hexadécimal comme insuffisants. Un identifiant encodé se décode, s'incrémente et se réencode en quelques secondes. Le seul effet obtenu est de retarder la découverte du défaut.
La fonction CloudPagesURL prend l'identifiant de la page puis des paires nom et valeur, et renvoie l'adresse avec une chaîne de requête chiffrée. Selon la documentation, cette chaîne porte aussi une référence à l'email d'origine, ce qui rend les chaînes de personnalisation utilisables sur la page.
%%[
SET @adherentId = AttributeValue("AdherentId")
]%%
<a href="%%=RedirectTo(CloudPagesURL(4521, 'cle', @adherentId, 'v', '2026'))=%%">
Vérifier mes coordonnées
</a>
Le paramètre cle fait doublon avec le contexte transmis par le lien, et c'est voulu : il servira de second contrôle sur la page. Le paramètre v identifie la campagne et permettra de refuser les liens trop anciens. Aucune donnée n'apparaît en clair dans l'adresse obtenue.
Certains noms de paramètres sont réservés et ne peuvent pas être employés, parmi lesquels PAGEID, MID, JID, LID, SID, JSB et URLID. Deux précisions pour les autres canaux : MicrositeURL joue le même rôle pour les microsites du contenu classique, et sur un SMS ou une notification, la page renvoie une erreur lorsque le destinataire n'est pas présent dans All Subscribers.
Côté page, deux fonctions lisent les paramètres reçus. La documentation officielle décrit RequestParameter et QueryParameter de la même façon, et présente la seconde comme conservée pour la compatibilité. Utilisez la première partout, le code se relit mieux. Une valeur absente ne provoque pas d'erreur, la fonction renvoie une chaîne vide, ce qui est pratique pour tester et dangereux si le code enchaîne sur une recherche sans vérifier.
Le module de sécurité de Trailhead pose des règles simples qui suffisent à éviter l'incident. Toute validation se fait côté serveur. Une page qui manipule des données demande une authentification. On vérifie avec au moins deux paramètres que c'est bien le même abonné qui interagit avec la page. Une page publique commence par un contrôle global qui arrête tout si un paramètre obligatoire manque.
%%[
SET @cle = RequestParameter("cle")
SET @version = RequestParameter("v")
SET @ctxCle = _subscriberkey
SET @acces = "refuse"
SET @motif = ""
IF Empty(@cle) OR Empty(@ctxCle) THEN
SET @motif = "parametre_absent"
ELSEIF RegExMatch(@cle, "^[0-9]{6,10}$", 0) == "" THEN
SET @motif = "format_invalide"
ELSEIF @cle != @ctxCle THEN
SET @motif = "incoherence_contexte"
ELSEIF @version != "2026" THEN
SET @motif = "lien_perime"
ELSE
SET @rows = LookupRows("Adherents", "AdherentId", @cle)
IF RowCount(@rows) == 1 THEN
SET @acces = "ok"
SET @ligne = Row(@rows, 1)
ELSE
SET @motif = "adherent_inconnu"
ENDIF
ENDIF
]%%
Chaque condition a un rôle précis. La première bloque l'ouverture directe de la page sans lien. La deuxième refuse une valeur qui n'a pas la forme d'un identifiant. La troisième vérifie que le paramètre et le contexte désignent la même personne, ce qui est le contrôle qui manquait dans l'incident. La dernière s'assure que la ligne existe, et une seule fois.
En cas de refus, gardez le motif pour le journal et affichez un message neutre, identique quel que soit le motif. Écrire à l'écran qu'un adhérent est introuvable confirme à un tiers qu'un identifiant existe ou non, et réaffiche une valeur reçue sans traitement. Le journal, lui, ne contient ni la clé reçue ni l'adresse IP : il sert à mesurer le volume de refus par motif, pas à identifier des personnes. Une hausse brutale du motif d'incohérence après un envoi signale un lien mal construit, une hausse du motif de format sans envoi récent signale plutôt quelqu'un qui teste la page.
💡 N'affichez que ce que vous avez lu : dans la branche autorisée, la page n'affiche que des valeurs issues de la data extension, jamais des valeurs reçues dans la requête. Un paramètre réaffiché tel quel est la porte d'entrée classique du cross-site scripting.
Le chiffrement empêche de fabriquer un lien ou d'en modifier le contenu. Il n'empêche pas de l'utiliser. Un email transféré à un collègue, une boîte partagée, un téléphone prêté, et la page s'ouvre exactement comme pour son destinataire. La question à poser en conception n'est donc pas « ce lien est-il chiffré » mais « que voit une personne qui possède ce lien sans être la bonne ».
La réponse dicte le contenu de la page. Un lien chiffré convient pour afficher peu de données et pour des actions à faible enjeu, des préférences de communication par exemple. Pour des données sensibles ou une action à fort impact, renvoyez vers l'espace client authentifié ou demandez une seconde vérification. Entre les deux, masquez partiellement ce que vous affichez, un numéro de téléphone réduit à ses deux derniers chiffres suffit à confirmer qu'il est à jour.
Reste la couche HTTP, qui se règle en tête de page et se teste en une fois. Salesforce recommande de servir les pages en HTTPS et d'ajouter des en-têtes de sécurité : Strict-Transport-Security avec une durée, X-Frame-Options à Deny, X-Content-Type-Options à nosniff et Referrer-Policy à strict-origin-when-cross-origin, ce dernier évitant justement que les sites externes reçoivent l'adresse complète de la page. L'en-tête Content-Security-Policy mérite un traitement à part, parce que la valeur la plus restrictive bloque aussi les scripts et les feuilles de style chargés depuis un CDN. Établissez d'abord la liste des domaines dont la page a besoin, testez sur une copie, publiez ensuite.
Dans une application web d'Adobe Campaign, le préchargement du destinataire passe souvent par un identifiant chiffré dans le lien, et la page refuse l'accès si le déchiffrement échoue. La fonction CloudPagesURL rend le même service sur le paramètre lui-même, qui n'est ni lisible ni modifiable.
La différence tient au contrôle. Chez Adobe, le préchargement est une option de l'application web et le refus est géré par la plateforme. Dans une CloudPage, rien ne bloque l'affichage à votre place : si la chaîne est vide, le code s'exécute quand même et la page s'affiche, vide, en invitant à saisir des coordonnées qui ne seront rattachées à personne. Le contrôle en tête de page est à écrire vous-même, comme une vérification dans un script serveur.
Ouvrez une de vos CloudPages publiques, regardez son adresse, et changez un caractère du paramètre. Si la page affiche autre chose que votre message de refus, vous avez le même défaut que la mutuelle. Le test prend trente secondes et se refait après chaque mise en production.
Non : Salesforce indique de ne transmettre ni SubscriberID, ni Subscriber Key, ni Contact Key en clair, et de ne pas inclure de données personnelles comme l'adresse email dans les liens. Une URL ne reste pas dans le navigateur du destinataire : elle passe dans l'historique, les emails transférés, les journaux des proxys d'entreprise, les outils d'analyse et l'en-tête envoyé aux sites appelés par la page. Tout ce qu'elle contient doit être considéré comme connu de tiers.
Non, ce n'est pas un chiffrement. Salesforce cite expressément cet encodage et la conversion en hexadécimal comme insuffisants. Un identifiant encodé se décode, s'incrémente et se réencode en quelques secondes. Le seul effet obtenu est de retarder la découverte du défaut.
La fonction CloudPagesURL prend l'identifiant de la page puis des paires nom et valeur, et renvoie l'adresse avec une chaîne de requête chiffrée. Selon la documentation, cette chaîne porte aussi une référence à l'email d'origine, ce qui rend les chaînes de personnalisation utilisables sur la page. Aucune donnée n'apparaît en clair dans l'adresse obtenue, et une chaîne altérée ne se déchiffre pas. Pour les microsites du contenu classique, MicrositeURL joue le même rôle.
Le module de sécurité de Trailhead demande que toute validation se fasse côté serveur, qu'on vérifie avec au moins deux paramètres que c'est bien le même abonné qui interagit avec la page, et qu'une page publique commence par un contrôle global qui arrête tout si un paramètre obligatoire manque. En pratique, on refuse le paramètre absent, le format invalide et l'incohérence entre le paramètre et le contexte, puis on vérifie que la ligne existe, et une seule fois. En cas de refus, on garde le motif pour le journal et on affiche un message neutre, identique quel que soit le motif.
Le chiffrement empêche de fabriquer un lien ou d'en modifier le contenu, mais il n'empêche pas de l'utiliser : un email transféré, une boîte partagée ou un téléphone prêté, et la page s'ouvre exactement comme pour son destinataire. La question à poser en conception est donc ce que voit une personne qui possède ce lien sans être la bonne. Un lien chiffré convient pour afficher peu de données et pour des actions à faible enjeu, tandis que des données sensibles ou une action à fort impact demandent l'espace client authentifié ou une seconde vérification.
Je peux auditer vos CloudPages, vos liens sortants et vos contrôles d'accès, et vous rendre un diagnostic écrit avec les correctifs à appliquer et leur ordre de priorité.
Me décrire votre contexte →