Nous avons créé un moteur de rendu d'e-mails HTML sûr et riche : voici comment
Avant, rien n'allait bien, et voici pourquoi
Dans le monde du courrier électronique, la qualité de l'affichage des messages HTML laisse à désirer. Les principaux problèmes sont liés aux différences de prise en charge du HTML et du CSS dans les différents clients de messagerie et interfaces web. Il arrive souvent que des messages électroniques qui semblent parfaits dans un service soient mal affichés dans un autre. Les utilisateurs sont naturellement contrariés, le taux de conversion chute et l'image de la marque en pâtit.
Cette situation s'explique en grande partie par le fait qu'il n'existe qu'une poignée de services monopolistiques dans le monde qui disposent d'une interface web pour l'affichage des courriels, et seulement quelques clients de messagerie populaires. Gmail et Yahoo n'ont pas beaucoup changé la façon dont ils affichent les courriels depuis des années, même si HTML et CSS se sont considérablement améliorés au fil du temps et offrent désormais de nombreuses nouvelles options pour l'affichage du contenu. Par exemple, la conception adaptative est apparue pour la première fois avec la mise en œuvre des media queries il y a environ 10 à 12 ans, la prise en charge du thème sombre en CSS est apparue il y a environ 4 à 5 ans, mais à ce jour, aucun des grands services de messagerie ne prend en charge ces fonctionnalités. Ils n'ont tout simplement aucune raison de changer quoi que ce soit - au lieu de cela, ce sont les webmasters qui doivent s'adapter aux caprices et aux limitations non documentées de ces services.
Par exemple, certains prennent en charge les animations et d'autres non. Gmail ne prend même pas en charge son propre format d'image webp
(inventé et popularisé par Google), alors qu'iCloud le prend en charge. Et il y a des centaines d'exemples comme celui-ci. Les webmasters sont obligés de revivre les premiers jours de l'Internet, lorsque la guerre des navigateurs faisait rage et qu'il fallait faire des pieds et des mains pour que les pages web se ressemblent dans les différents navigateurs.
Certes, certaines restrictions, comme l'absence de prise en charge des scripts, sont parfois dictées par des préoccupations de sécurité - vous ne voudriez pas recevoir un courrier électronique contenant une bannière interactive qui envoie vos données directement à un réseau publicitaire. Mais le plus souvent, l'ensemble des limitations semble absolument inadéquat - quel mal de simples images d'arrière-plan, définies avec background-image
, ont-elles fait à Gmail ?
Notre idée
En faisant les premiers pas sur le marché de l'email avec AdGuard Temp Mail, nous avons passé beaucoup de temps à étudier les options d'affichage des emails. Nous ne voulions absolument pas devenir un autre Gmail, en inventant notre propre sous-ensemble HTML/CSS et en réduisant tous les composants que nous aurions jugés inutiles. Mais nous n'avions pas non plus l'intention de laisser les utilisateurs seuls face aux menaces du web.
Chapitre 1. Sept chaînes de code JavaScript qui ont résolu le problème
Heureusement, le monde du développement web ne reste pas inactif et les développeurs de navigateurs travaillent depuis longtemps sur les sandbox de navigateurs, dont nous avons profité.
Qu'est-ce qu'un sandbox de navigateur ? Il s'agit essentiellement d'un ensemble de techniques visant à restreindre les capacités de la page web, littéralement une liste de choses à faire et à ne pas faire très similaire au CSP (nous l'utilisons également, mais nous y reviendrons plus tard) : "ne pas utiliser les cookies de la page mère", "ne pas exécuter de JavaScript", "ne pas envoyer de référent HTTP lorsque quelqu'un suit un lien à partir du cadre" et un certain nombre d'autres directives. Cela semble intéressant, n'est-ce pas ? Entrons dans les détails de la mise en œuvre technique.
Nous recevons le corps de l'e-mail de l'API sous la forme d'une chaîne contenant des balises HTML, que nous affichons ensuite dans le navigateur en utilisant les capacités du sandbox :
const html = '<html><body>....</body></html>';
const iframe = document.createElement('iframe');
iframe.credentialless = true;
iframe.sandbox = 'allow-popups allow-popups-to-escape-sandbox';
iframe.referrerpolicy = 'no-referrer';
iframe.srcdoc = html;
document.body.appendChild(iframe);
Ajoutez quelques styles, positionnez le cadre sur la page parente - et le tour est joué, vous venez de créer un moteur robuste et sûr pour afficher des courriels HTML sans limitations inutiles !
Dans un monde parfait, ce serait la fin de l'histoire. Après tout, n'avons-nous pas arrêté la principale menace - JavaScript ? Malheureusement, les courriels modernes contiennent souvent une montagne de traceurs de toutes sortes, qui visent à collecter autant de données que possible sur vous. Il s'agit de zero-pixels - des images invisibles qui transmettent votre adresse IP et des données sur votre appareil à leurs serveurs - et de liens de suivi spéciaux qui enregistrent toutes les actions que vous effectuez dans l'e-mail. Nous avons dû trouver des solutions à tous ces problèmes.
Chapitre 2. Anonymisation de l'utilisateur
Au cours du développement, nous sommes tombés sur un excellent instrument pour évaluer la sécurité de notre moteur - emailprivacytester.com.
Ce service génère un courriel HTML qui contient un certain nombre d'éléments spécifiquement conçus pour tester certaines vulnérabilités. Nous pouvons maintenant lancer le processus de diagnostic, nous envoyer un e-mail à une boîte aux lettres TempMail d'AdGuard et... observer de multiples fuites d'adresses IP.
Tout comme nous avons traité JavaScript et d'autres problèmes similaires plus tôt, concentrons-nous maintenant sur ces fuites d'adresses IP. Une fuite d'adresse IP se produit lorsque l'expéditeur de l'e-mail peut déterminer votre adresse IP simplement en envoyant l'e-mail. Bien sûr, si vous utilisez AdGuard VPN, tout ce que l'expéditeur obtiendra sera l'adresse IP du serveur VPN auquel vous êtes connecté. Mais faites-le ne serait-ce qu'une fois sans la protection d'un VPN, et l'auteur sera en mesure de connaître votre géolocalisation jusqu'à la ville. Bien entendu, nous ne pouvions pas laisser une telle chose arriver à nos utilisateurs. Alors, que faire ?
Images en proxy
Heureusement, il n’y avait aucune raison de réinventer la roue. Tout cela a déjà été fait auparavant, et tous les « grands » fournisseurs de services de messagerie le font depuis longtemps : ils encapsulent toutes les images dans leur propre proxy.
Comment? Par exemple, si votre e-mail contenait cette image :
<img src="http://my.website/image/cat.jpg" />
donc à chaque fois que vous ouvrez l'e-mail, le propriétaire du serveur my.website
reçoit une requête HTTP. Les métadonnées de cette requête contiendront votre adresse IP, ainsi que des données supplémentaires sur votre appareil, notamment votre système d'exploitation, la version de votre navigateur et d'autres détails qui pourraient potentiellement vous désanonymiser.
Comment fonctionne le proxy ? Il remplace l'attribut src
de toutes les balises d'image par sa propre URL, comme indiqué ci-dessous :
<img src="https://email.provider/image?url=http://my.website/image/cat.jpg" />
Le serveur web email.provider
est conçu pour télécharger chaque image source une seule fois. Ensuite, il enregistre l'image et les requêtes suivantes ne sont pas vues par le propriétaire du serveur my.website
. Même pour la requête initiale, my.website
voit seulement qu'elle est faite par email.provider
et n'enregistre que son adresse IP, pas de données personnelles. C'est bien et tout, sauf que dans ce cas, ces données sont collectées par email.provider
lui-même (et nous nous souvenons que c'est souvent une grande entreprise comme Google ou Yahoo qui joue ce rôle).
Nous avons développé une solution similaire. Cependant, contrairement à d'autres, notre proxy d'image ne collecte ni ne stocke les données de l'utilisateur et est complètement anonyme.
Après avoir mis en place le proxy, nous devons maintenant « envelopper » d'une manière ou d'une autre toutes les images contenues dans l'e-mail, et pour cela, nous devons considérer notre source HTML comme un DOM. Nous ne pouvons pas travailler avec le DOM à l'intérieur du bac à sable puisqu'il ne contient pas de JavaScript. Nous ne pouvons pas y exécuter JavaScript, même pour notre propre code, car cela ouvrirait la porte à l'exécution de JavaScript pour tout autre code dans les courriels entrants. Et si nous devons travailler avec le DOM en dehors du sandbox, nous aimerions avoir la garantie que le HTML est irréprochable.
Heureusement, nous avons trouvé une bibliothèque étonnante : DOMPurify. Il peut « nettoyer » le contenu HTML et le rendre sûr et structuré. Il a été largement testé et a été à juste titre salué par les experts en sécurité. Commençons par l'utiliser :
const html = '<html><body>....</body></html>';
const clean = DOMPurify.sanitize(html, {
// restore partial HTML to a full-fledged document, if needed
WHOLE_DOCUMENT: true,
// these tags make no sense in the context of an HTML email, so we’re deleting them
FORBID_TAGS: ['audio', 'video', 'button', 'input', 'form'],
});
const iframe = document.createElement('iframe');
// the set of actions with the sandbox from step one
iframe.srcdoc = clean;
document.body.appendChild(iframe);
Génial, nous avons réussi à éliminer le désordre, nous pouvons maintenant revenir au traitement de l'image. DOMPurify va nous aider ici aussi ! Il dispose d'un excellent mécanisme d'accroche - nous pouvons résoudre le problème suivant sans changer de contexte :
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
if (node.tagName === 'IMG' && node.hasAttribute('src')) {
// changing the original src to a link to ImageProxy
const src = wrapWithImageProxy(node.getAttribute('src'));
node.setAttribute('src', src);
}
});
Il est maintenant temps d'exécuter à nouveau les diagnostics, et nous constatons que le nombre de fuites d'adresses IP a diminué... mais qu'il n'est pas tombé à zéro. Pourquoi ? C'est le moment de parler de CSS.
Fuites d'adresses IP par le biais de CSS
CSS offre des capacités de stylisation du contenu vraiment impressionnantes. Aucun autre système n'arrive à la cheville de CSS - sa simplicité, sa facilité d'utilisation et la variété de ses fonctionnalités en font l'une des meilleures inventions de l'ingénierie logicielle. Cependant, comme beaucoup d'outils puissants, certaines de ses caractéristiques peuvent être mal utilisées. Dans le contexte de notre tâche, la partie "nuisible" est la fonction url()
, qui télécharge le contenu via un lien pour styliser la page. L'exemple le plus courant est celui des images d'arrière-plan. Le code pour les télécharger ressemble à ceci :
background-image: url(http://my.website/image/cat.jpg);
Les problèmes sont identiques à ceux décrits dans le chapitre précédent, à savoir les fuites d'IP. Outre les images d'arrière-plan, la fonction url()
, telle qu'elle est détaillée dans la documentation CSS de Mozilla, peut être utilisée dans diverses règles CSS. Il y en a beaucoup, et la syntaxe du sélecteur CSS permet de définir des règles relativement complexes, comme le sélecteur de l'un des exemples du lien ci-dessus :
background-image: cross-fade(20% url(http://my.website/image/first.png), url(http://my.website/image/second.png));
L'analyse d'une telle règle en toute sécurité n'est pas chose aisée, il faut des outils adéquats. Et nous avons encore eu de la chance et trouvé exactement le bon outil pour ce travail ! csstree fait précisément ce dont nous avons besoin - il permet d'analyser le CSS et de modifier certains de ses types de sélecteurs. En code, cela ressemble à ceci :
csstree.walk(ast, (node) => {
if (node.type === 'Url') {
node.value = wrapWithImageProxy(node.value);
}
});
27 / 5 000
ce qui nous donne ce résultat :
background-image: cross-fade(20% url(https://img.agrd.eu/image?url=http://my.website/image/first.png), url(https://img.agrd.eu/image?url=http://my.website/image/second.png));
Voilà, finies les fuites d’adresses IP !
CSP
La protection des données est un domaine qui ne peut tolérer aucune exception. Pas une seule. C'est pourquoi il est impératif de trouver une solution pour les cas où une vulnérabilité serait découverte dans l'une des bibliothèques ou dans notre code qui interpréterait incorrectement les données. La dernière ligne de défense, quand tout le reste échoue. Dans notre cas, nous utilisons le seul et unique CSP dans ce rôle. CSP est un ensemble de directives permettant un contrôle granulaire de l'accès des pages à certaines ressources. Les webmasters ont souvent du mal avec le CSP. Il est facile d'oublier que certaines nouvelles ressources nécessaires à la page web ne sont pas autorisées dans les paramètres CSP - pour être tout à fait honnête, nous avons également commis cette erreur par le passé. Mais nous avons réussi à apprivoiser CSP avec une solution de ce type :
// restrict image sources to only those wrapped inside our proxy
img-src data: https://img.agrd.eu/image;
// disallow connections to any sources not explicitly permitted above
script-src 'none';
style-src 'none';
font-src 'none';
connect-src 'none';
media-src 'none';
object-src 'none';
prefetch-src 'none';
child-src 'none';
frame-src 'none';
worker-src 'none';
frame-ancestors 'none';
...
// which can be simplified as
default-src 'none';
Nous sommes désormais en sécurité : même si l'une des bibliothèques est mal utilisée ou si le code de la fonction de transformation de l'adresse de l'image est défaillant, CSP ne permettra pas au navigateur d'envoyer une requête au serveur de l'auteur de l'infraction et de divulguer votre adresse IP.
Les résultats de notre travail sur la confidentialité des données
Grâce aux capacités des navigateurs modernes et à quelques excellentes bibliothèques, nous sommes parvenus à un affichage sûr et de haute qualité des courriels HTML, sans aucune fuite d'IP, ni JavaScript, ni aucun autre contenu potentiellement dangereux - tout est entièrement sous notre contrôle.
Pourquoi est-ce si génial ?
Nous avons abordé de nombreux sujets liés à la sécurité, détournant peut-être l'attention d'une réalisation cruciale : nous sommes parvenus à créer des courriels HTML compatibles à 100 % avec HTML5/CSS3, ce que personne d'autre au monde n'a réussi à faire ! Cela signifie que vous avez à votre disposition toutes les possibilités offertes par les pages HTML modernes : animation, adaptabilité, formats d'image modernes, thèmes sombres tels qu'ils ont été conçus (ne détestez-vous pas aussi les affreux changements de couleur dus à des algorythmes primitifs ?) Vous n'avez plus à subir des mises en page complexes utilisant des tableaux comme dans les années 90, mais vous pouvez au contraire concevoir facilement de magnifiques courriels adaptatifs pour les appareils mobiles.
C'est fou qu'au milieu des années 2020, les principaux fournisseurs de messagerie affichent encore des courriels comportant de nombreuses failles et vulnérabilités. Nous sommes extrêmement heureux d'avoir réussi à créer une alternative sûre qui répond à toutes les exigences d'un client de messagerie moderne.
Si quelque chose tourne mal
Nous sommes fiers de tout ce que nous avons réalisé avec AdGuard Temp Mail jusqu'à présent, mais nous sommes également conscients que nous ne sommes qu'au début de notre voyage. Lorsque vous créez un nouveau produit, vous ne pouvez pas éviter toutes les erreurs et tous les défauts. Si vous identifiez une vulnérabilité dans l'affichage des courriels, veuillez la signaler à security@adguard.com. Nous la traiterons dans les plus brefs délais et vous pourrez prétendre à une récompense dans le cadre de notre programme Bug Bounty.
Notre équipe de développeurs prend très au sérieux les commentaires des utilisateurs. Ainsi, si vous remarquez que votre courrier électronique HTML s'affiche de manière incorrecte, veuillez nous envoyer un message à support@adguard.com, et nous traiterons le problème dans les plus brefs délais.