DEV Community

Cover image for Cloudflare Is Not Enough: Two Security Gaps We Still Find Behind the WAF
Stanley A.
Stanley A.

Posted on • Originally published at wardenbit.com

Cloudflare Is Not Enough: Two Security Gaps We Still Find Behind the WAF

Most teams I speak with have Cloudflare in front of their site. That is usually a good thing — Cloudflare absorbs DDoS traffic, proxies requests, applies managed WAF rules, and makes a messy internet-facing stack much easier to operate.

But one dangerous assumption often follows:

"We are behind Cloudflare, so we are protected."

That is not how it works. Cloudflare only protects traffic that actually flows through Cloudflare. And even when traffic does flow through it, a WAF does not automatically fix missing authentication, exposed development environments, broken authorization, SQL injection, or business logic flaws.

Here are two patterns we still find regularly during blackbox web application testing. What makes this more pressing today: with AI-assisted tooling, the reconnaissance and exploitation steps across both scenarios can be chained and executed in a matter of minutes — work that once took hours of manual effort.


1. The origin server is still reachable directly

A common Cloudflare mistake is moving DNS behind the orange cloud but leaving the origin server exposed to the public internet.

Attackers do not need magic to find this. They use the same passive sources defenders should already be checking:

# Certificate transparency logs
curl -s "https://crt.sh/?q=shop.example.com&output=json" | jq '.[].name_value' | sort -u

# Historical DNS from SecurityTrails, DNSDB, ViewDNS, etc.
# Look for old A records that pre-date the Cloudflare migration.

# Shodan check once an origin candidate is found
shodan host <origin-IP>
Enter fullscreen mode Exit fullscreen mode

Once a candidate IP is identified, a direct request with the correct Host header can confirm whether Cloudflare is being bypassed. The -k flag is used here to skip certificate validation, since the origin cert may not match the hostname:

curl -vk --resolve shop.example.com:443:<origin-IP> https://shop.example.com/
Enter fullscreen mode Exit fullscreen mode

The response headers tell you immediately where you are. Through Cloudflare, you would expect to see its fingerprints:

< Server: cloudflare
< CF-RAY: 7a1b2c3d4e5f6789-LHR
Enter fullscreen mode Exit fullscreen mode

Direct to origin, you might instead see:

< HTTP/1.1 200 OK
< Server: nginx/1.22.1 (Ubuntu)
< X-Powered-By: PHP/8.1.2
< Content-Type: text/html; charset=UTF-8
Enter fullscreen mode Exit fullscreen mode

At that point, the WAF is not in the path at all. Any exposed admin panel, forgotten upload endpoint, missing rate limit, or application bug is reachable directly.

Directory discovery against the origin might look like this. The Host header is required so the server routes the request correctly:

ffuf -w /usr/share/wordlists/dirb/common.txt \
     -u https://<origin-IP>/FUZZ \
     -H "Host: shop.example.com" \
     -mc 200,301,302,403 \
     -o results.json
Enter fullscreen mode Exit fullscreen mode

One recurring finding is an admin login that the team expected to be protected by Cloudflare, but is actually accessible directly on the origin. If the application also lacks login throttling, the risk compounds quickly:

for i in {1..20}; do
  curl -s -o /dev/null -w "%{http_code}\n" -X POST https://<origin-IP>/admin/login \
    -H "Host: shop.example.com" \
    -d "username=admin&password=test$i"
done

# 200
# 200
# 200
# ...no 429, no lockout, no CAPTCHA
Enter fullscreen mode Exit fullscreen mode

The important point here is not "Cloudflare failed." Cloudflare was never in the request path to begin with.


2. Cloudflare is in the path, but the application is still vulnerable

The second pattern is subtler.

Sometimes Cloudflare is configured correctly. Production DNS is proxied. The origin firewall only allows Cloudflare IP ranges. Direct origin access is blocked.

But a development or staging subdomain is publicly reachable.

A typical discovery flow:

# Certificate transparency enumeration
curl -s "https://crt.sh/?q=%.example.com&output=json" | jq '.[].name_value' | sort -u

# Passive multi-source subdomain discovery
subfinder -d example.com -silent -o subdomains.txt

# HTTP host probing
ffuf -w /usr/share/wordlists/subdomains-top1million.txt \
     -u https://FUZZ.example.com \
     -mc 200,301,302,403 \
     -t 50
Enter fullscreen mode Exit fullscreen mode

Then a hostname appears:

dev.example.com
Enter fullscreen mode Exit fullscreen mode

It is orange-clouded. It resolves through Cloudflare. The origin is not directly reachable.

But the dev application loads with no VPN, no basic auth, no IP allowlist, and no authentication prompt.

At this point, Cloudflare is doing exactly what it was configured to do: proxy traffic to a publicly available hostname.

If that dev environment has debug mode enabled, the application may leak more than intended. A deliberately malformed request:

curl -s "https://dev.example.com/api/v1/accounts?account_id='"
Enter fullscreen mode Exit fullscreen mode

Might return:

HTTP/1.1 500 Internal Server Error

Error: SQLSTATE[42000]: Syntax error or access violation near ''' at line 1
Query: SELECT * FROM users WHERE account_id = ''' AND status = 'active'
File: /var/www/dev/api/v1/accounts.php, Line 88
Enter fullscreen mode Exit fullscreen mode

That single response exposes the stack, file paths, parameter names, and the full query shape.

A time-based injection check can then confirm exploitability. SLEEP() is MySQL-specific; adjust accordingly for other databases:

curl -s "https://dev.example.com/api/v1/accounts?account_id=1' AND SLEEP(5)-- -"
Enter fullscreen mode Exit fullscreen mode

A WAF can catch some common SQL injection patterns. But it is not a replacement for parameterised queries, input validation, proper access control, and secure environment configuration. In this scenario, every request went through Cloudflare. The vulnerability still existed.


The shared lesson

These are different failure modes:

  • In the first case, Cloudflare is bypassed entirely because the origin is publicly exposed.
  • In the second case, Cloudflare is correctly in the path, but it proxies traffic into an unsafe application.

Both produce the same practical outcome: something sensitive is reachable from the public internet while the team assumes it is covered.


Checks worth running

If you operate a Cloudflare-protected application, these are a good starting point:

  • Audit DNS and certificate history. Check crt.sh and historical DNS sources for old origin IPs that predate the Cloudflare migration.
  • Test old origin IPs directly. If any candidate still responds on port 80 or 443, investigate whether it bypasses the WAF.
  • Restrict origin inbound traffic. Configure your origin firewall (or security group) to only accept connections from Cloudflare's published IP ranges.
  • Consider Cloudflare Tunnel. Rather than exposing your origin on a public IP at all, Cloudflare Tunnel creates an outbound-only connection from your server to Cloudflare, eliminating the direct exposure problem entirely.
  • Enumerate all DNS records, not just production. Dev, staging, preview, and internal subdomains are all worth reviewing.
  • Protect non-production environments. Put dev and staging behind VPN, an IP allowlist, or at minimum HTTP basic auth. None of these should be open to the public internet.
  • Disable debug mode outside controlled environments. Verbose error output is a significant information leak.
  • Rotate or audit exposed secrets. Dev environments often contain API keys, database credentials, or internal service URLs in error output or config endpoints. Treat any exposure as a potential leak.
  • Test application logic directly. IDOR, broken access control, SQL injection, authentication flows, and exposed admin paths all require application-layer testing that a WAF cannot perform for you.

Cloudflare is a genuinely useful tool. WAFs are worth having.

But neither tells you what is actually reachable on your infrastructure, nor how your application behaves once an attacker gets there. That requires knowing your own attack surface — and testing it.


Originally published on WardenBit

Top comments (0)