Cloud

Azure WAF : lire les blocages Application Gateway avec KQL sans partir dans tous les sens

Construire des requêtes KQL utiles pour identifier les requêtes bloquées par Azure Web Application Firewall sur Application Gateway, avec action, ruleId, URI, client IP, hostname et fenêtre temporelle.

25 mai 2026 azurewafapplication-gatewaykqllog-analyticssecurity

Un WAF en mode prévention peut bloquer une requête légitime comme une attaque réelle. Le plus difficile n’est pas toujours de voir qu’un blocage existe, mais de comprendre rapidement ce qui a été bloqué, par quelle règle, depuis quelle source et sur quel endpoint. Sans méthode, l’analyse dérive vite : on regarde le WAF, puis le backend, puis le DNS, puis les certificats, alors que les logs contiennent déjà une partie de la réponse.

Le scénario traité ici est celui d’un Azure Application Gateway avec Web Application Firewall activé et des logs envoyés vers Log Analytics. L’objectif est de construire quelques requêtes KQL de base pour lire les blocages, regrouper les événements et préparer une investigation propre avant de modifier une règle.

Cas fil rouge : un portail partenaire bloqué après livraison

L’exemple suivi dans l’article est un portail partenaire publié sur partner.example.com. Le backend fonctionne, les probes Application Gateway sont saines, mais un partenaire signale qu’un formulaire de commentaire échoue depuis 09:15 avec une page d’erreur WAF. L’équipe dispose de trois éléments : l’IP source 203.0.113.10, l’endpoint /partner/comment, et une fenêtre d’incident d’environ une heure.

L’objectif n’est pas encore de créer une exclusion. Il faut d’abord prouver que le blocage vient bien du WAF, identifier la règle, mesurer si le problème touche un seul partenaire ou tout le formulaire, puis fournir une preuve relisible à l’équipe applicative et sécurité.

Partir de la bonne table

Selon la configuration de diagnostic et le mode de collecte, les logs peuvent apparaître dans des tables différentes. Dans beaucoup d’environnements historiques, les événements WAF Application Gateway sont dans AzureDiagnostics avec Category == "ApplicationGatewayFirewallLog". Dans des configurations plus récentes, des tables spécifiques aux ressources peuvent exister. Il faut d’abord confirmer où les événements arrivent.

kusto 01-find-waf-logs.kql
search "ApplicationGatewayFirewallLog"
| summarize count() by $table
| order by count_ desc

Cette requête ne remplace pas la connaissance de la configuration, mais elle évite de chercher dans la mauvaise table. Une fois la table confirmée, les requêtes doivent être adaptées au schéma réellement disponible.

Lister les blocages récents

La première requête utile doit rester simple : période courte, catégorie WAF, action bloquante, champs exploitables. Le but est de voir les événements récents sans encore décider s’il s’agit d’un faux positif.

kusto 02-recent-blocks.kql
AzureDiagnostics
| where TimeGenerated > ago(2h)
| where Category == "ApplicationGatewayFirewallLog"
| where action_s =~ "Blocked"
| project TimeGenerated,
        hostname_s,
        requestUri_s,
        clientIp_s,
        ruleSetType_s,
        ruleSetVersion_s,
        ruleId_s,
        message_s,
        details_message_s,
        policyId_s
| order by TimeGenerated desc

Les noms exacts des colonnes peuvent varier selon la table et la configuration. Si une colonne n’existe pas, il faut inspecter le schéma et adapter. L’important est de garder ensemble l’heure, l’URI, l’IP client, l’action, la règle et le message.

Regrouper par règle et URI

Un événement isolé est rarement suffisant. Pour prioriser, il faut regrouper. Une même règle qui bloque une seule requête suspecte n’a pas le même poids qu’une règle qui bloque toutes les soumissions d’un formulaire métier depuis la mise en production.

kusto 03-blocks-by-rule-uri.kql
AzureDiagnostics
| where TimeGenerated > ago(24h)
| where Category == "ApplicationGatewayFirewallLog"
| where action_s =~ "Blocked"
| summarize blocks=count(),
          firstSeen=min(TimeGenerated),
          lastSeen=max(TimeGenerated),
          sampleMessage=any(message_s)
by hostname_s, requestUri_s, ruleId_s, ruleSetType_s, ruleSetVersion_s
| order by blocks desc

Cette vue aide à distinguer un bruit de fond d’un vrai problème applicatif. Si un ruleId apparaît massivement sur un endpoint précis, il faut examiner ce que cet endpoint reçoit avant de créer une exception.

Corréler avec une IP ou une fenêtre d’incident

Quand un utilisateur ou un partenaire signale un blocage, l’investigation doit partir d’une fenêtre horaire, d’une IP source, d’un hostname ou d’un chemin. Plus la requête est ciblée, plus le résultat est exploitable.

kusto 04-incident-window.kql
let startTime = datetime(2026-05-25 09:00:00);
let endTime = datetime(2026-05-25 10:00:00);
let sourceIp = "203.0.113.10";
AzureDiagnostics
| where TimeGenerated between (startTime .. endTime)
| where Category == "ApplicationGatewayFirewallLog"
| where clientIp_s == sourceIp
| project TimeGenerated,
        action_s,
        hostname_s,
        requestUri_s,
        ruleId_s,
        message_s,
        details_message_s
| order by TimeGenerated asc

Ce format donne une chronologie. Il permet de voir si la requête a été détectée, bloquée, répétée, ou si plusieurs règles se déclenchent sur le même parcours.

Transformer le résultat en preuve exploitable

Pour le portail partenaire, la sortie attendue doit pouvoir être copiée dans un ticket d’incident. Une ligne du type 09:18, partner.example.com, /partner/comment, 203.0.113.10, Blocked, 942100, SQL Injection Attack Detected est beaucoup plus utile qu’une capture d’écran du portail Azure. Elle permet à l’équipe applicative de retrouver le formulaire, à l’équipe sécurité de comprendre la règle, et au run de vérifier si le problème s’est répété.

text incident-evidence.txt
Incident WAF
Application : partner.example.com
Endpoint : /partner/comment
Source : 203.0.113.10
Fenêtre : 09:00 - 10:00
Action : Blocked
Règle : 942100
Volume : 18 blocages sur 42 minutes
Hypothèse : champ texte libre déclenchant une règle SQLi

Cette preuve ne décide pas encore de la correction. Elle donne simplement un socle commun pour la suite : faux positif, attaque réelle, correction applicative ou exclusion ciblée.

Regarder les règles qui détectent avant de bloquer

En mode détection, les logs ne signifient pas que la requête a été bloquée. En mode prévention, certains événements peuvent contribuer au score ou être journalisés selon le ruleset et la politique. L’analyse doit donc distinguer action et impact réel.

kusto 05-actions-summary.kql
AzureDiagnostics
| where TimeGenerated > ago(24h)
| where Category == "ApplicationGatewayFirewallLog"
| summarize events=count() by action_s, ruleId_s, message_s
| order by events desc

Cette requête évite une erreur classique : traiter tout événement WAF comme un blocage. Avant de changer une règle, il faut confirmer que l’action a réellement empêché la requête attendue.

Conclusion

Lire les blocages WAF avec KQL doit commencer simple : trouver la bonne table, filtrer les actions bloquantes, projeter les champs utiles, puis regrouper par règle, URI, source et période. Le but n’est pas de créer une exception rapidement. Le but est d’obtenir une preuve lisible.

Une bonne investigation WAF répond à quatre questions : quelle requête a été bloquée, par quelle règle, sur quel endpoint, et avec quel volume. Tant que ces réponses ne sont pas claires, modifier OWASP, CRS ou DRS revient à traiter un symptôme sans comprendre le risque.