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é
if (req.http.X-Bot == false
&& var.is_blocked == false
&& req.http.host ~ “example.com”) {
// Logique métier
// BUG : toujours exécuté !
}Corrigé
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 ?
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
Attendu
Réel (Fastly)
| Aspect | Varnish (open-source) | Fastly VCL |
|---|---|---|
| Type de parser | Descente 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 parser | Ne liste que () ! && || |
| == inter-types | Vérification stricte des types | « Égalité définie pour tous les types » |
Table de vérité : résultats attendus vs. réels
| X-Bot | is_blocked | Attendu | Réel |
|---|---|---|---|
| false | false | true | true |
| false | true | false | true✗ |
| true | false | false | true✗ |
| true | true | false | true✗ |
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).
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 == false → true — 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).
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.
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.
Autres analyses
TLS vs mTLS Handshake
Comparaison des flux d'authentification TLS standard et mutuel
Sécurité réseauTLS 1.2 vs TLS 1.3 Handshake
Comparaison de l'efficacité du handshake et des améliorations de sécurité
Ingénierie réseauRéseau IPv6-Only avec NAT64/464XLAT
Faire fonctionner un réseau local IPv6-only tout en maintenant la connectivité IPv4
IA & IndustrieQuelque 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 & IndustrieOrchestration 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éseauDNSSEC : 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éseauDé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 SecurityVotre 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.