Cloud

Azure WAF: qualify a false positive before creating an exclusion

A practical method to analyze an Azure WAF block, isolate the rule involved, compare application evidence, and decide between a fix, a targeted exclusion, or a custom rule.

29 May 2026 azurewaffalse-positiveowaspkqlrunbook

A WAF false positive is not just a legitimate request being blocked. It is a security decision that must be explained before it is bypassed. In Azure Application Gateway WAF, the temptation is often to quickly create an exclusion on a parameter, an OWASP/CRS rule, or an application path. That response can be correct, but only if the diagnosis proves that the block comes from legitimate and repeatable content.

The running case is simple: an internal application published behind Application Gateway WAF sometimes blocks submission of a business form. Users see an error, the application team says the data is normal, and operations must decide whether to fix the application, adjust a managed rule, create an exclusion, or handle the problem differently.

The goal is not to make the WAF silent. The goal is to reduce an unjustified block without losing a useful signal.

Start with the exact event

The first step is to find the WAF event that matches the user error. Avoid broad searches such as “everything blocked yesterday”. A false positive is qualified with a short time window, a hostname, a path, a source address, or a correlation identifier.

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

This query does not try to solve the problem yet. It fixes the object of diagnosis: which request, which rule, which message, which rule version, and which transaction ID. Without that base, an exclusion may target too broadly.

Separate reproducible blocking from opportunistic noise

Not every WAF alert deserves an exclusion. If the block is an isolated request, with no identified user, no application impact, and a clearly suspicious payload, the team can keep the signal. Conversely, if the same form fails every time with normal business data, the diagnosis becomes a priority.

text qualification-questions.txt
Qualification questions
Does the block match a real user incident?
Is the application path known and expected?
Is the blocked data produced by the application or freely entered?
Is the same scenario reproducible outside production?
Does the WAF rule block a precise field or the whole request?
Was there a recent application change on this form?

This step protects against two opposite errors: ignoring a real operational problem or weakening the WAF to hide a doubtful request.

Read the rule before excluding it

The ruleId_s field gives a clue, but it is not enough. You need to understand what the rule is trying to detect: SQL injection, XSS, abnormal protocol behavior, suspicious encoding, excessive size, or something else. A rule triggered by a JSON body does not call for the same correction as a rule triggered by a cookie name.

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

If the rule hits several paths and several sources, the problem may not be limited to the form. If it only hits one precise field on one endpoint, a targeted exclusion can become reasonable. The real scope matters more than the first ticket impression.

Compare with application logs

A reliable WAF diagnosis cross-checks WAF logs with application logs. The WAF says it blocked a request. The application should confirm whether it received it, rejected it afterwards, or never saw it because the block happened before business logic.

text evidence-to-collect.txt
Evidence to compare
WAF transaction or precise timestamp
HTTP path and method
Status returned to the client
User or session identifier if available
Matching application log
Deployed application version
Minimal payload that reproduces the block

This comparison avoids treating a classic application issue as a false positive: server-side validation, size limit, inconsistent encoding, schema change, or serialization error. The WAF can be visible in the incident without being the main cause.

Reproduce with a minimal payload

Before creating an exclusion, reduce the case to the smallest payload that still triggers the rule. This is often the most useful step. It shows whether the issue comes from a field, a character sequence, encoding, or the whole structure.

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": "business value that triggers the rule",
  "reference": "TEST-WAF-001"
}'

The test should be run in an environment intended for this or during a controlled diagnostic window. The idea is not to replay sensitive data. Build a representative, cleaned example that helps the team understand the behavior.

Choose between fix, exclusion, and custom rule

Once the evidence is gathered, the decision becomes clearer. If the application sends unnecessarily ambiguous content, an application fix is preferable. If the rule blocks a legitimate and stable business field, a targeted exclusion may be acceptable. If the real need is to control who accesses a path or method, a custom rule is often better suited than an exclusion.

text decision-matrix.txt
Observation
A free-text field sometimes contains sequences interpreted as SQLi
Likely decision
Targeted exclusion on the field and rule, after proof of legitimacy

Observation
The application encodes JSON poorly or double-encodes a value
Likely decision
Application fix before WAF adjustment

Observation
The /admin path is called from the Internet
Likely decision
Access custom rule, not an OWASP exclusion

Observation
A whole rule blocks several endpoints without analysis
Likely decision
Do not disable globally, segment the diagnosis

A good exclusion is small: one rule, one field, one path, or one clearly justified context. The broader it is, the more it must be temporary, documented, and reviewed.

Document the exception as a security change

A WAF exclusion is not just an operational fix. It changes the detection surface. It should therefore be documented with proof of the false positive, the exact scope, the rule involved, the application owner, and a review condition.

text waf-exclusion-record.txt
WAF exclusion
Date: 2026-05-29
Application: internal portal
Hostname: app.example.internal
Path: /api/forms/submit
Rule: OWASP/CRS ruleId to confirm in logs
Field: commentaire
Justification: reproducible business payload, confirmed by application logs
Scope: exclusion limited to the field and rule
Review: after application fix or next WAF review

This trace makes the change operable. It also makes it possible to revisit the decision when the application evolves, the rule version changes, or the form is replaced.

Monitor after the change

Diagnosis does not stop when the exclusion is created. Verify that the business scenario works, the rule no longer blocks the false positive, and the volume of accepted requests does not become abnormal. A successful exclusion reduces one precise block. It should not silently open a new path.

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

If blocks disappear but other rules trigger on the same endpoint, the exclusion may have treated only a symptom. If traffic increases sharply, check that the change did not mask automated behavior.

Conclusion

Qualifying an Azure WAF false positive requires more than identifying a noisy rule. You need to tie the block to a real incident, understand the rule, reduce the payload, cross-check application logs, and choose the right lever. A targeted exclusion is sometimes the right answer, but it comes at the end of the diagnosis, not at the beginning.

This discipline keeps the WAF useful for operations. Teams can fix legitimate blocks without turning the policy into a collection of opaque exceptions. The expected result is simple: fewer false positives, more evidence, and rules that remain understandable during the next incident.