GraphQL False Positives After Raising OWASP Paranoia
HelioDocs is a B2B SaaS on Cloudflare Business. Its main API is a single POST /graphql endpoint accepting JSON bodies up to 256 KB. After enabling the OWASP Core Ruleset at paranoia level 2 with an anomaly threshold of 40, 7% of legitimate mutation requests from a large customer return 403. WAF analytics show hits on SQL-injection and XSS rules because document bodies contain sample strings such as 'SELECT patient_name' and '
1. Diagnose the root cause and lay out a safe tuning sequence.
Read the worked answer
The OWASP Core Ruleset is inspecting JSON bodies and scoring strings that look like SQL and HTML. At paranoia level 2 with an anomaly threshold of 40, a legitimate document containing sample code can exceed the threshold and produce a 403. The /upload path is unaffected because the rule scope likely matches the GraphQL endpoint or the body is not inspected the same way.
Safe sequence: first set the OWASP ruleset to log/simulate so nothing is blocked while data is collected. Use WAF analytics to identify the specific rule IDs and the exact request fields that trigger them. Create scoped skip exceptions for those rule IDs on /graphql only, not a blanket disable. Lower paranoia to level 1 and raise the anomaly threshold to a tested value such as 60. Replay real customer payloads in staging or through log review, then re-enable block mode. Keep the Cloudflare Managed Ruleset active and monitor 403 rates by rule ID.
2. Would you disable the OWASP ruleset on /graphql entirely? Defend your answer.
Read the worked answer
No. Disabling OWASP on /graphql removes protection for every GraphQL request, including traffic from customers that never triggered a false positive. It also creates a blind spot that an attacker can target once the path is known to be unprotected. The better control is a scoped exception: keep the ruleset enabled, skip only the noisy rule IDs for /graphql, and leave the rest of the ruleset in block mode. If exceptions grow too broad, build a custom ruleset for the GraphQL path with a smaller set of high-confidence rules and log the rest.
The exception must be reviewed. If a rule is skipped because a document contains 'SELECT', an attacker can hide SQL injection in the same field. Pair the exception with application-level query allowlisting and schema validation so the WAF is not the only defense.
3. What compensating controls would you add for GraphQL-specific abuse that the WAF handles poorly?
Read the worked answer
GraphQL abuse often looks like valid JSON, so the WAF cannot reliably distinguish a deep query from a legitimate one. Add application controls: maximum query depth, maximum query cost or complexity, alias and batch limits, and disabled introspection in production. Use persisted or allowlisted queries where possible. Enforce request body size limits at the edge and reject operations that exceed the cost budget before execution.
At the edge, add a custom WAF rule to block known malicious operation names and oversized bodies, and rate-limit by authenticated customer or operation if that identifier is present in a header or cookie. Log schema violations and high-cost queries. These controls catch abuse that anomaly scoring misses and reduce the chance that tuning the WAF becomes an exercise in disabling protection.