Cloud

Azure WAF : qualifier un faux positif avant de créer une exclusion

Méthode pratique pour analyser un blocage Azure WAF, isoler la règle concernée, comparer les preuves applicatives et décider entre correction, exclusion ciblée ou custom rule.

29 mai 2026 azurewaffalse-positiveowaspkqlrunbook

Un faux positif WAF n’est pas seulement une requête légitime bloquée. C’est une décision de sécurité qu’il faut pouvoir expliquer avant de la contourner. Dans Azure Application Gateway WAF, la tentation est souvent de créer rapidement une exclusion sur un paramètre, une règle OWASP/CRS ou un chemin applicatif. Cette réponse peut être correcte, mais seulement si le diagnostic montre que le blocage vient bien d’un contenu légitime et répétable.

Le cas fil rouge est simple : une application interne publiée derrière Application Gateway WAF bloque parfois l’envoi d’un formulaire métier. Les utilisateurs voient une erreur, l’équipe applicative indique que la donnée est normale, et l’exploitation doit décider s’il faut modifier l’application, ajuster une règle managée, créer une exclusion ou traiter le problème autrement.

L’objectif n’est pas de rendre le WAF silencieux. L’objectif est de réduire un blocage injustifié sans perdre un signal utile.

Commencer par l’événement exact

La première étape consiste à retrouver l’événement WAF qui correspond à l’erreur utilisateur. Il faut éviter les recherches trop larges du type “tout ce qui a été bloqué hier”. Un faux positif se qualifie avec une fenêtre courte, un hostname, un chemin, une adresse source ou un identifiant de corrélation.

kusto find-blocked-request.kql
AzureDiagnostics
| where TimeGenerated between (datetime(2026-05-29 09:00:00) .. datetime(2026-05-29 09:20:00))
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| where hostname_s == "app.example.internal"
| project TimeGenerated,
        hostname_s,
        requestUri_s,
        clientIp_s,
        ruleSetType_s,
        ruleSetVersion_s,
        ruleId_s,
        message_s,
        details_message_s,
        transactionId_g
| order by TimeGenerated desc

Cette requête ne cherche pas encore à résoudre le problème. Elle sert à fixer l’objet du diagnostic : quelle requête, quelle règle, quel message, quelle version de règles et quel identifiant de transaction. Sans cette base, une exclusion risque de viser trop large.

Séparer blocage reproductible et bruit opportuniste

Toutes les alertes WAF ne méritent pas une exclusion. Si le blocage ne concerne qu’une requête isolée, sans utilisateur identifié, sans impact applicatif et avec un payload clairement suspect, l’équipe peut simplement conserver le signal. À l’inverse, si le même formulaire échoue à chaque soumission avec une donnée métier normale, le diagnostic devient prioritaire.

text qualification-questions.txt
Questions de qualification
Le blocage correspond-il à un incident utilisateur réel ?
Le chemin applicatif est-il connu et attendu ?
La donnée bloquée est-elle produite par l'application ou saisie librement ?
Le même scénario est-il reproductible hors production ?
La règle WAF concernée bloque-t-elle un champ précis ou toute la requête ?
Existe-t-il un changement applicatif récent sur ce formulaire ?

Cette étape protège contre deux erreurs opposées : ignorer un vrai problème d’exploitation ou affaiblir le WAF pour masquer une requête douteuse.

Lire la règle avant de l’exclure

Le champ ruleId_s donne une indication, mais il ne suffit pas. Il faut comprendre ce que la règle tente de détecter : injection SQL, XSS, protocole anormal, encodage suspect, taille excessive ou autre comportement. Une règle qui réagit sur un corps JSON n’appelle pas la même correction qu’une règle qui réagit sur un nom de cookie.

kusto group-by-rule.kql
AzureDiagnostics
| where TimeGenerated > ago(24h)
| where Category == "ApplicationGatewayFirewallLog"
| where hostname_s == "app.example.internal"
| summarize hits=count(),
          blocked=countif(action_s == "Blocked"),
          sampleUri=any(requestUri_s),
          sampleMessage=any(message_s),
          sampleDetails=any(details_message_s)
by ruleSetType_s, ruleSetVersion_s, ruleId_s
| order by blocked desc, hits desc

Si la règle touche plusieurs chemins et plusieurs sources, le problème n’est peut-être pas limité au formulaire. Si elle ne touche qu’un champ précis d’un seul endpoint, une exclusion ciblée peut devenir raisonnable. Le périmètre réel compte plus que l’impression donnée par le ticket initial.

Comparer avec les logs applicatifs

Un diagnostic WAF fiable croise les logs WAF avec les logs de l’application. Le WAF dit qu’il a bloqué une requête. L’application doit confirmer si elle l’a reçue, si elle l’a rejetée ensuite, ou si le blocage est bien intervenu avant toute logique métier.

text evidence-to-collect.txt
Preuves à rapprocher
Transaction WAF ou horodatage précis
Chemin HTTP et méthode
Code retourné au client
Identifiant utilisateur ou session si disponible
Log applicatif correspondant
Version de l'application déployée
Payload minimal permettant de reproduire le blocage

Cette comparaison évite de traiter comme faux positif un problème applicatif classique : validation côté serveur, limite de taille, encodage incohérent, changement de schéma ou erreur de sérialisation. Le WAF peut être visible dans l’incident sans en être la cause principale.

Reproduire avec un payload minimal

Avant de créer une exclusion, il faut réduire le cas au plus petit payload qui déclenche encore la règle. Cette étape est souvent la plus utile. Elle montre si le problème vient d’un champ, d’une combinaison de caractères, d’un encodage ou d’une structure complète.

bash replay-minimal-request.sh
APP_URL="https://app.example.internal/api/forms/submit"

curl -i "$APP_URL" -H "Content-Type: application/json" -H "X-Diagnostic-Run: waf-fp-20260529" --data '{
  "commentaire": "valeur metier qui declenche la regle",
  "reference": "TEST-WAF-001"
}'

Le test doit être réalisé dans un environnement prévu pour cela ou avec une fenêtre de diagnostic maîtrisée. L’idée n’est pas de rejouer des données sensibles. Il faut construire un exemple représentatif, nettoyé, qui permet à l’équipe de comprendre le comportement.

Choisir entre correction, exclusion et custom rule

Une fois les preuves réunies, la décision devient plus claire. Si l’application envoie un contenu inutilement ambigu, la correction applicative est préférable. Si la règle bloque un champ métier légitime et stable, une exclusion ciblée peut être acceptable. Si le besoin réel est de contrôler qui accède à un chemin ou à une méthode, une custom rule est souvent plus adaptée qu’une exclusion.

text decision-matrix.txt
Observation
Un champ libre contient parfois des sequences interpretees comme SQLi
Decision probable
Exclusion ciblee sur le champ et la regle, apres preuve de legitimite

Observation
L'application encode mal un JSON ou double-encode une valeur
Decision probable
Correction applicative avant ajustement WAF

Observation
Le chemin /admin est appele depuis Internet
Decision probable
Custom rule d'acces, pas exclusion OWASP

Observation
Une regle complete bloque plusieurs endpoints sans analyse
Decision probable
Ne pas desactiver globalement, segmenter le diagnostic

La bonne exclusion est petite : une règle, un champ, un chemin ou un contexte clairement justifié. Plus elle est large, plus elle doit être temporaire, documentée et revue.

Documenter l’exception comme un changement de sécurité

Une exclusion WAF n’est pas un simple correctif d’exploitation. Elle modifie la surface de détection. Elle doit donc être documentée avec la preuve du faux positif, le périmètre exact, la règle concernée, le propriétaire applicatif et une condition de revue.

text waf-exclusion-record.txt
Exclusion WAF
Date : 2026-05-29
Application : portail interne
Hostname : app.example.internal
Chemin : /api/forms/submit
Regle : OWASP/CRS ruleId a confirmer dans les logs
Champ : commentaire
Justification : payload metier reproductible, confirme par logs applicatifs
Portee : exclusion limitee au champ et a la regle
Revue : apres correction applicative ou prochaine revue WAF

Cette trace rend le changement exploitable. Elle permet aussi de revenir dessus lorsque l’application évolue, que la version de règles change ou que le formulaire est remplacé.

Surveiller après modification

Le diagnostic ne s’arrête pas à la création de l’exclusion. Il faut vérifier que le scénario métier fonctionne, que la règle ne bloque plus le faux positif, et que le volume de requêtes acceptées ne devient pas anormal. Une exclusion réussie réduit un blocage précis. Elle ne doit pas ouvrir silencieusement un nouveau passage.

kusto post-change-monitoring.kql
AzureDiagnostics
| where TimeGenerated > ago(6h)
| where Category == "ApplicationGatewayFirewallLog"
| where hostname_s == "app.example.internal"
| where requestUri_s has "/api/forms/submit"
| summarize total=count(),
          blocked=countif(action_s == "Blocked"),
          matchedRules=dcount(ruleId_s),
          sampleRule=any(ruleId_s),
          sampleMessage=any(message_s)
by bin(TimeGenerated, 30m)
| order by TimeGenerated asc

Si les blocages disparaissent mais que d’autres règles se déclenchent sur le même endpoint, l’exclusion n’a peut-être traité qu’un symptôme. Si le trafic augmente fortement, il faut vérifier que le changement n’a pas masqué un comportement automatisé.

Conclusion

Qualifier un faux positif Azure WAF demande plus qu’identifier une règle bruyante. Il faut rattacher le blocage à un incident réel, comprendre la règle, réduire le payload, croiser les logs applicatifs et choisir le bon levier. Une exclusion ciblée est parfois la bonne réponse, mais elle arrive à la fin du diagnostic, pas au début.

Cette discipline garde le WAF utile pour l’exploitation. Les équipes peuvent corriger les blocages légitimes sans transformer la policy en collection d’exceptions opaques. Le résultat attendu est simple : moins de faux positifs, plus de preuves, et des règles encore compréhensibles au prochain incident.