Une faille d'injection HTML stockée dans le tableau d'offres d'emploi RamenHire a permis à n'importe qui de remplir un formulaire public et de transformer les e-mails de notification de l'équipe en une plateforme de phishing.
Comment la faille s'est glissée
RamenHire fonctionne avec Next.js et Supabase et permet aux startups de publier des offres sans se connecter. Chaque soumission déclenche l'envoi d'un e-mail à l'équipe de recrutement interne. Le corps de l'e-mail est construit en assemblant directement des champs de formulaire bruts — nom de l'entreprise, description du poste, nom du contact — dans une chaîne HTML. Aucun échappement ni aucune désinfection n'est effectué avant que la chaîne n'atteigne le service de messagerie.
Comme le modèle traite les entrées utilisateur comme du HTML sûr, un candidat malveillant peut injecter un faux lien « cliquez ici pour vérifier ». La charge utile est stockée sur le serveur en tant que partie de l'annonce d'emploi, de sorte que chaque e-mail de notification ultérieur contient le code malveillant. Un attaquant peut masquer ce lien à côté du véritable bouton « Voir le tableau de bord », incitant un membre de l'équipe à révéler ses identifiants ou à visiter un site de phishing.
Pourquoi c'était important
Les e-mails sont envoyés à l'équipe de recrutement principale, des personnes qui cliquent constamment sur des liens pour faire progresser les candidats dans le processus.
La lacune technique
L'échappement HTML n'est pas une étape unique. Deux contextes nécessitent une protection :
- Nœuds de texte – le contenu entre les balises. Convertir
<en<et>en>empêche l'utilisation de balises, mais n'empêche pas un attaquant de sortir de la valeur d'un attribut. - Valeurs d'attribut – les chaînes à l'intérieur des balises telles que
href="…". L'injection d'un guillemet ("ou') permet à un attaquant de fermer l'attribut prématurément et d'insérer son propre balisage.
Le code original ne gérait que le texte brut, laissant l'injection d'attributs totalement ouverte.
Résolution du problème
Le développeur a ajouté une petite fonction utilitaire escapeHtml et l'a appliquée à chaque valeur fournie par l'utilisateur avant l'interpolation, que la valeur se retrouve dans un nœud de texte ou dans un attribut.
Étapes de vérification
- Tests locaux – Une série de chaînes d'injection a été injectée dans les fonctions du modèle. Chaque test n'a affiché que des caractères échappés, sans aucune balise exécutable.
- Tests en production – Après le déploiement du correctif, une charge utile a été soumise via le site en direct, en passant le contrôle de bot Cloudflare Turnstile. L'e-mail résultant est arrivé dans la boîte de réception du développeur ; le lien malveillant et les éventuelles balises
<script>sont apparus sous forme de texte illisible, et non comme des éléments cliquables.
Note concernant Gmail : le service peut encore colorer une URL en bleu et la rendre cliquable, mais il s'agit d'une commodité côté client et cela ne signifie pas que l'injection HTML a réussi. Le correctif empêche l'injection de créer de véritables boutons ou d'exécuter des scripts.
Ce que les développeurs doivent surveiller
- Ne faites jamais confiance aux données provenant de formulaires publics – Même des formulaires d'apparence inoffensive peuvent produire des sorties stockées utilisées ailleurs.
- Échappez les données au moment de l'utilisation – Appliquez un échappement sensible au contexte (texte vs attribut) juste avant le rendu, et non plus tôt dans le pipeline.
- Testez à la fois le côté client et le côté serveur – Les tests unitaires détectent les échecs évidents ; un test manuel de bout en bout via l'interface utilisateur en direct confirme que les défenses tiennent face au trafic réel.
- Soyez attentifs aux particularités des clients de messagerie – Certains clients créent automatiquement des liens pour les URL, ce qui peut donner un faux sentiment de sécurité. Vérifiez que le HTML sous-jacent ne contient aucun élément actif.
L'essentiel
Un simple oubli dans la gestion des entrées utilisateur a transformé des e-mails de notification de routine en un vecteur de phishing. L'introduction d'une routine complète d'échappement HTML et la validation du correctif localement et en production ont restauré l'intégrité de la boîte de réception. Cet épisode souligne une leçon intemporelle pour toute application web générant du HTML à partir de données non fiables : une désinfection appropriée est non négociable, et l'ignorer peut ouvrir une ligne directe vers la boîte de réception de vos utilisateurs.
