Aller au contenu

Liens autorisés et verrouillage du contenu

Votre clé publique est visible dans le code de votre site : n’importe qui peut la copier et appeler l’API directement. Pour un formulaire de contact, ce n’est pas un problème : l’e-mail arrive toujours chez vous. C’est différent lorsqu’un modèle prend son destinataire dans les paramètres (confirmation d’inscription à une newsletter, réponse automatique, confirmation de commande) : quiconque détient la clé publique pourrait alors envoyer votre modèle – depuis votre domaine, avec votre identité visuelle – à n’importe quelle adresse, en choisissant le texte et les liens. C’est exactement ainsi que fonctionne le phishing.

La règle de Frontmail est donc : destinataire dynamique ⇒ contenu verrouillé. Vous pouvez toujours envoyer à un destinataire choisi dans le navigateur, mais l’appelant ne peut pas transformer votre modèle en e-mail de phishing.

Requête Verrouillé
clé publique + modèle dont To/CC/BCC utilisent des paramètres (Autoriser un destinataire dynamique activé) oui
réponse automatique (part toujours vers l’adresse du formulaire) oui – liens et champs d’expéditeur
clé privée (votre serveur) non, sauf si le modèle a N’autoriser que les liens vers des domaines autorisés
clé publique + modèle avec un destinataire fixe (formulaire de contact) non – seule la règle sur l’expéditeur s’applique

Un modèle où Autoriser un destinataire dynamique est activé mais dont To/CC/BCC contiennent des adresses fixes n’est pas verrouillé.

  1. Le CAPTCHA est obligatoire. Le modèle doit avoir Exiger un CAPTCHA (Turnstile) activé, sinon la requête échoue avec 403 dynamic_recipient_requires_captcha. Le CAPTCHA utilise vos propres clés Turnstile. Si vous ne pouvez pas utiliser de CAPTCHA, envoyez le modèle depuis votre serveur avec une clé privée.
  2. Expéditeur fixe. L’adresse d’expéditeur (From), le nom d’expéditeur et Reply-To ne doivent pas utiliser de paramètres – 403 dynamic_field_not_allowed.
  3. Pas de balisage de l’appelant, textes courts. Les paramètres de type HTML ne peuvent pas être remplis, et chaque valeur texte fait au plus 500 caractères (paramètres URL : 2 048 ; une Longueur max. plus petite définie sur le paramètre s’applique toujours) – 422 invalid_template_params avec details.reason: "content_locked".
  4. Liens uniquement vers des domaines autorisés – voir ci-dessous – 403 link_not_allowed.
  5. Limite par destinataire. Au maximum 5 e-mails par adresse de destinataire et par jour (UTC), réglable dans Sécurité → E-mails par destinataire et par jour (1–1 000). Au-delà : 429 rate_limited avec details.scope: "recipient". Les réponses automatiques partagent ce compteur – au-delà de la limite, aucune réponse automatique n’est créée.

Toutes les vérifications ont lieu avant qu’un crédit soit consommé. Pour les réponses automatiques, une vérification échouée marque la réponse automatique comme failed (avec le code d’erreur dans l’historique), le crédit est remboursé et rien n’est envoyé.

Un lien est autorisé lorsque son domaine est :

  • écrit directement dans le modèle (HTML, version texte ou objet) – et non produit par un placeholder. https://acme.com/reset?token={{token}} autorise acme.com ; {{reset_url}} ou https://{{sub}}.acme.com n’autorisent rien ;
  • dans votre liste de domaines autorisés (Sécurité → Sites autorisés) ;
  • dans Sécurité → Domaines de liens autorisés – un par ligne, example.com (exactement cet hôte) ou *.example.com (sous-domaines, sans le domaine principal) – selon les mêmes règles que les domaines autorisés ;
  • le domaine de Frontmail lui-même (le pied de page Envoyé via Frontmail du forfait Free).

mailto:, tel: et tout autre lien non web (javascript:, data:, …) ne sont autorisés que lorsque exactement le même lien est écrit dans le modèle.

Après le rendu, Frontmail collecte chaque URL qu’un client de messagerie pourrait ouvrir ou transformer en lien :

  • href, src, srcset, action, formaction, background, poster et les autres attributs d’URL, y compris dans les commentaires conditionnels d’Outlook ;
  • url() et @import CSS dans les attributs style et dans <style>, ainsi que <meta http-equiv="refresh"> ;
  • les URL et les domaines nus dans le texte HTML, la version texte et l’objet – les clients de messagerie transforment automatiquement evil.com/login ou www.evil.com en liens, ils comptent donc aussi comme des liens.

Avant la comparaison, les URL sont normalisées comme un navigateur les lirait : les entités HTML et les échappements CSS sont décodés, les caractères invisibles supprimés, \ traité comme /, les URL relatives au protocole //host résolues, les informations d’utilisateur retirées (https://acme.com@evil.com correspond à evil.com), l’encodage en pourcentage décodé, les majuscules et les points finaux supprimés, et les noms de domaine internationalisés comparés en punycode – un sosie comme аcme.com avec un а cyrillique ne correspond pas à acme.com (l’erreur le signale avec idn: true).

L’erreur ne liste que les domaines en cause dans details.hosts – jamais les URL complètes, qui peuvent contenir des jetons.

  • Les domaines nus sont détectés à l’aide d’une liste de terminaisons connues qui ne sont pas des domaines (report.pdf, index.html et Node.js ne sont pas des liens) ; un texte comme Mr.Smith peut donc compter comme le domaine mr.smith. Les terminaisons prisées du phishing qui sont aussi de vrais domaines (.zip, .mov) comptent toujours.
  • Les simples adresses e-mail dans le texte (jane@example.com) ne sont pas traitées comme des liens.
  • La vérification porte sur la destination des liens, pas sur le texte qui les entoure. Gardez des textes courts et une limite par destinataire basse pour les formulaires publics.

Activez N’autoriser que les liens vers des domaines autorisés dans les réglages du modèle (Paramètres → Protection) pour appliquer la vérification des liens à chaque requête, y compris les envois avec clé privée. L’option est activée par défaut pour les modèles de la galerie avec un destinataire dynamique.

Pour chaque requête avec clé publique, l’adresse d’expéditeur (From) du modèle ne doit pas venir des paramètres (sinon 403 dynamic_field_not_allowed). Un formulaire de contact qui place l’adresse du visiteur dans Reply-To continue de fonctionner – c’est d’ailleurs là qu’elle doit aller. L’éditeur de modèles vous avertit de tout cela dans Vérifications.