Ingénierie CDN

Le piège == false en Fastly VCL

Comment l'utilisation de == false au lieu de ! dans des conditions composées en Fastly VCL peut silencieusement casser votre logique

Laurent Goudet · 10 février 2026 · 8 min de lecture

Le bug

Une condition Fastly VCL avec deux == false dans une chaîne && s’évaluait silencieusement à true, contournant un filtre de domaine. La condition était censée restreindre la logique à un domaine spécifique, mais elle s’appliquait à tout le trafic.

L’ingénieur a passé des heures à déboguer, isolant chaque sous-condition individuellement. Chaque condition fonctionnait correctement seule. Le bug ne se manifestait que lorsque

deux comparaisons == false ou plus

étaient chaînées avec &&.

Cassé

Deux == false dans une chaîne && — condition toujours vraie
if (req.http.X-Bot == false
    && var.is_blocked == false
    && req.http.host ~ “example.com”) {
  // Logique métier
  // BUG : toujours exécuté !
}

Corrigé

Remplacer un == false par ! — la condition fonctionne correctement
if (req.http.X-Bot == false
    && !var.is_blocked
    && req.http.host ~ “example.com”) {
  // Logique métier
  // Fonctionne correctement
}

L’investigation

Nous avons fouillé la documentation VCL de Fastly à la recherche d’une explication. Ce que nous avons trouvé était surprenant :

la table de priorité des opérateurs de Fastly ne liste que quatre opérateurs

: (), !, && et ||. Les opérateurs de comparaison comme ==, !=, ~ et > sont documentés dans une section séparée, sans aucun niveau de priorité défini.

En revanche, le parser VCL open-source de Varnish (dont le VCL de Fastly est dérivé) a une hiérarchie de descente récursive claire où == a une priorité plus élevée que &&. L’omission dans la documentation de Fastly soulevait la question : le fork de Fastly gère-t-il la priorité des opérateurs différemment ?

Constat : priorité des opérateurs manquante

La référence VCL officielle de Fastly documente la priorité des opérateurs dans une table à quatre niveaux : ()!&&||. Les opérateurs de comparaison ( ==, !=, ~, >, <, etc.) sont totalement absents de cette table.

Dans le parser open-source de Varnish (vcc_expr.c), les fonctions de descente récursive suivent une hiérarchie stricte : vcc_expr_cmp (comparaisons) est appelé depuis vcc_expr_not, ce qui signifie que == a naturellement une priorité plus élevée que &&. Si le fork de Fastly dévie de cette hiérarchie, a == false && b pourrait être analysé comme a == (false && b).

Comparaison des arbres d'analyse

Comment la priorité des opérateurs change le sens d'une même expression

Attendu

== a une priorité plus élevée que &&
&&
==
X-Bot
false
==
blocked
false

Réel (Fastly)

&& pourrait avoir une priorité plus élevée que ==
==
==
X-Bot
&&
false
blocked
false
AspectVarnish (open-source)Fastly VCL
Type de parserDescente récursive (vcc_expr.c)Fork propriétaire
Précédence == vs &&✓ Plus élevée (bien définie)✗ Non documentée
Table de prioritéImplicite dans la hiérarchie du parserNe liste que () ! && ||
== inter-typesVérification stricte des types« Égalité définie pour tous les types »

Table de vérité : résultats attendus vs. réels

Trois des quatre combinaisons d'entrée produisent un résultat erroné
X-Botis_blockedAttenduRéel
falsefalsetruetrue
falsetruefalsetrue
truefalsefalsetrue
truetruefalsetrue

La preuve

Nous avons déployé un PoC dédié sur un service Fastly en production pour confirmer le bug. Le point de test exécute quatre variantes de la même chaîne && où les trois conditions devraient s’évaluer à false — la troisième condition vérifie req.http.Host == “www.nonexistent-domain.example, qui ne peut jamais correspondre. Aucun des blocs ne devrait s’exécuter.

$ curl -sD- https://www.syd1.fln-dev.net/_vcl-bug-test{“\n”} x-test1-both-eqfalse: REACHED-BUG x-test2-both-bang: not-reached x-test3-mixed: not-reached x-test4-parens: not-reached

Les quatre tests et leurs résultats :

Test

Schéma

Résultat

1

STRING == false && BOOL == false

REACHED-BUG

2

!STRING && !BOOL

not-reached

3

STRING == false && !BOOL

not-reached

4

(STRING == false) && (BOOL == false)

not-reached

Le test 1 confirme le bug : deux comparaisons == false dans une chaîne && font que la condition entière s’évalue à true, indépendamment des conditions suivantes. Les tests 2 à 4 montrent trois contournements indépendants : utiliser ! pour les deux (Test 2), mélanger == false avec ! (Test 3), ou encadrer chaque comparaison avec des parenthèses explicites (Test 4).

Constat : la précédence seule n'explique pas tout

Si == et && partagent la même priorité et se lient de gauche à droite, l’expression A == false && B == false && (C == D) est analysée comme ((((A == false) && B) == false) && (C == D)). En déroulant l’évaluation : A == false → true, true && B → false, false == falsetrue — mais ensuite true && (C == D) → false. Le dernier && devrait ramener le résultat à false. Pourtant, le PoC montre que l’expression s’évalue à true.

Cela indique un problème de génération de code pour le court-circuit qui s’ajoute au problème de précédence. Les compilateurs optimisent couramment X == false dans une chaîne && en inversant la condition de branchement — au lieu de calculer la comparaison et de brancher sur le résultat, ils évaluent X directement et inversent la polarité du saut. Avec deux == false dans la même chaîne, la seconde inversion peut corrompre la cible du saut, provoquant le saut des conditions restantes et l’exécution directe du corps du bloc. C’est cohérent avec les quatre résultats du PoC : le bug nécessite exactement deux == false dans la chaîne (Test 1), un seul fonctionne correctement (Test 3), ! utilise un chemin de code entièrement différent (Test 2), et les parenthèses explicites forcent les comparaisons à être pré-évaluées avant d’entrer dans la chaîne de court-circuit (Test 4).

Constat : comparaison inter-types

Les en-têtes HTTP en VCL sont des chaînes. Écrire req.http.X-Bot == false compare une STRING à un BOOL. La documentation de Fastly indique « l’égalité est définie pour tous les types » mais ne spécifie pas ce qui se passe lorsque les types diffèrent.

Est-ce que “0” == false s’évalue à true ? Et "" == false ? Qu’en est-il d’un en-tête manquant ? Le système de types de Varnish est strict, mais le fork de Fastly pourrait gérer les comparaisons inter-types avec une conversion implicite. Ce comportement non documenté, combiné au problème de priorité, crée un champ de mines pour les conditions composées.

La correction

Remplacer un seul des deux == false par ! a suffi à corriger le bug. La condition finale utilise !var.is_blocked au lieu de var.is_blocked == false, tout en conservant la comparaison d’en-tête HTTP comme req.http.X-Bot == false.

Les parenthèses explicites autour de chaque comparaison fonctionnent également : (req.http.X-Bot == false) && (var.is_blocked == false) force le parser à grouper les comparaisons correctement, évitant le problème de précédence.

Note : Le code « corrigé » utilise toujours req.http.X-Bot == false, ce que les points clés ci-dessous signalent comme une mauvaise pratique (comparer un en-tête chaîne à un booléen). C’était le correctif minimal — remplacer un seul == false pour casser le problème de précédence composée. La pratique recommandée est d’utiliser ! partout : !req.http.X-Bot && !var.is_blocked.

Points clés pour Fastly VCL

Utilisez ! au lieu de == false.

L’opérateur de négation a une priorité bien définie dans la documentation de Fastly. L’opérateur d’égalité, non.

Ne comparez pas les en-têtes HTTP à des booléens. Les en-têtes sont des chaînes. Utilisez !req.http.X-Bot (teste l’existence/véracité) plutôt que req.http.X-Bot == false.

Soyez particulièrement prudent avec les conditions composées.

Le bug ne se manifestait qu’avec deux == false dans une chaîne. Les utilisations isolées semblaient fonctionner, rendant ce problème extrêmement difficile à détecter lors des tests.

Confirmé avec un PoC en production sur la plateforme VCL de Fastly en février 2026. Le bug a été signalé au support Fastly. La cause probable est une priorité non définie pour l’opérateur == combinée à un problème de génération de code pour le court-circuit lorsque plusieurs comparaisons == false apparaissent dans la même chaîne &&. Confirmation exacte en attente de Fastly.

Questions fréquentes

Pourquoi == false pose-t-il problème dans les conditions composées en Fastly VCL ?

La documentation VCL de Fastly ne définit pas la priorité de l'opérateur == par rapport à &&. Quand deux comparaisons == false sont chaînées avec &&, la condition peut être mal évaluée.

Comment inverser une condition en Fastly VCL en toute sécurité ?

Utilisez l'opérateur ! au lieu de == false. L'opérateur ! a une priorité bien définie dans la documentation de Fastly, contrairement à ==.

Peut-on comparer des en-têtes HTTP à des booléens en VCL ?

Les en-têtes sont des chaînes en VCL. Comparer une chaîne à un booléen (req.http.X-Bot == false) repose sur une conversion de type implicite non documentée. Utilisez !req.http.X-Bot à la place.

Laurent Goudet

CTO chez Freelancer.com

Agents IA, réseau et infrastructure à grande échelle

Autres analyses

Sécurité réseau

TLS vs mTLS Handshake

Comparaison des flux d'authentification TLS standard et mutuel

Sécurité réseau

TLS 1.2 vs TLS 1.3 Handshake

Comparaison de l'efficacité du handshake et des améliorations de sécurité

Ingénierie réseau

Réseau IPv6-Only avec NAT64/464XLAT

Faire fonctionner un réseau local IPv6-only tout en maintenant la connectivité IPv4

IA & Industrie

Quelque chose de grand se passe — mais pas ce que vous croyez

Pourquoi l'IA est un changement de couche d'abstraction, pas la fin du travail intellectuel — la réponse d'un praticien à l'essai viral sur l'IA

IA & Industrie

Orchestration d'agents IA à grande échelle — Ce qui fonctionne vraiment en production

Patterns et leçons tirées de l'exploitation de systèmes multi-agents à l'échelle de 80M+ utilisateurs : routage, chaînes de fallback, gestion du contexte, et pourquoi la plupart des architectures d'agents échouent.

Sécurité réseau

DNSSEC : chaîne de confiance de la racine à ce domaine

Comment DNSSEC construit une chaîne de confiance cryptographique de la racine DNS à cette zone — avec configuration Pulumi et vérification dig en direct

Sécurité réseau

Déployer DMARC à grande échelle

Guide pratique pour déployer DMARC sur une grande plateforme — corrections SPF, DKIM et alignement à travers AWS SES, Google Workspace, des relais Postfix et des dizaines de domaines

Cloud Security

Votre cle API Google Maps peut maintenant vider votre compte en banque

Google a silencieusement modifie les permissions des cles API pour que celles destinees a Maps puissent desormais appeler Gemini AI. Voici comment auditer vos projets GCP et verrouiller les cles exposees.

© 2026 Laurent Goudet · Bordeaux, France · lepro.dev

vd9714f4