Un WAF qui bloque mon client Go : diagnostiquer une empreinte TLS
le symptome
Mon application interrogeait un site de recettes public. Depuis mon poste comme
depuis le serveur, curl obtenait un 200 impeccable. Le même
appel depuis mon client Go : HTTP 400, systématiquement, sans corps
d'erreur exploitable.
eliminer les suspects un par un
La méthode qui a marché : ne changer qu'une variable à la fois.
IP du serveur ? curl depuis le VPS → 200. Ce n'est pas l'IP. User-Agent ? UA de Chrome copié dans le client Go → 400. Ce n'est pas l'UA. En-têtes ? jeu d'en-têtes identique à curl → 400. Ce n'est pas ça non plus. Le protocole ? c'est la dernière variable qui reste.
Quand IP, URL et en-têtes sont identiques et que le résultat diffère, la différence est sous HTTP. Restait la poignée de main TLS.
le fingerprinting TLS
Chaque client TLS envoie un ClientHello qui décrit ses capacités : versions supportées, suites de chiffrement, courbes elliptiques, extensions — et surtout leur ordre. Cette signature est stable par implémentation : le ClientHello de Go ne ressemble pas à celui d'OpenSSL, qui ne ressemble pas à celui de Chrome. C'est ce que résume une empreinte de type JA3.
Un WAF comme DataDome n'a donc pas besoin de lire l'en-tête User-Agent :
il constate qu'un client prétendant être Chrome présente le ClientHello de la
bibliothèque standard de Go, et bloque. Aucun en-tête ne peut corriger ça, puisque le
verdict tombe avant le premier octet de HTTP.
la tentative de contournement
Il existe une réponse technique : uTLS,
un fork de crypto/tls qui permet de reproduire le ClientHello d'un
navigateur. Le déroulé :
- ClientHello « Chrome » via uTLS → plus de 400, mais un
EOF. - Cause : le profil Chrome annonce
h2dans l'extension ALPN, alors que mon transport ne parlait que HTTP/1.1. Le serveur négociait HTTP/2 et je lui répondais en HTTP/1.1. - ALPN forcé à
http/1.1→ retour du400: en modifiant l'extension, j'avais changé l'empreinte, qui ne correspondait plus à un vrai Chrome. - Étape suivante : monter un
http2.Transportpar-dessus la connexion uTLS.
pourquoi j'ai arrete la
J'ai arrêté avant cette dernière étape, et c'est la partie de l'histoire qui compte le plus.
Techniquement, la course était gagnable — pour quelques semaines. Un profil uTLS se périme à chaque version de navigateur ; le WAF ajoute des signaux ; la maintenance devient permanente. Surtout, la question de fond n'était pas technique : un site qui déploie un WAF exprime une intention. Contourner activement une protection pour récupérer du contenu, c'est franchir une ligne que je ne veux pas franchir dans un projet personnel.
J'ai supprimé le code, retiré la dépendance uTLS, et remplacé la fonctionnalité par une base de recettes locale plus fournie, complétée par un import de données structurées schema.org/Recipe — que les sites publient volontairement pour les moteurs de recherche.
ce que je retiens
- Quand tout est identique et que le résultat diffère, descendre d'une couche. Le blocage n'était pas dans HTTP, mais dessous.
- L'empreinte TLS est un signal d'identité à part entière — utile à connaître autant en défense qu'en analyse d'incident.
- Savoir s'arrêter fait partie du métier. La meilleure solution ici n'était pas la plus astucieuse : c'était celle qui reste maintenable et légitime.