← Blog

Security

Cloudflare WAF-log lezen: vijf signaturen van intentie

Een Slack-ping om 22:14: 14.000 WAF-events in het afgelopen uur. De helft is ruis, een kwart is echt, de rest zijn je eigen regels die misvuren. Zo herken je het verschil.

Jacob Molkenboer· Oprichter · A Brand New Company· 16 jul 2024· 6 min
Koperen loep op gevouwen telegram, groene lakzegel op kaart, rood sleutellabel op ivoren bureau.

Het is dinsdagavond 22:14 en je telefoon trilt. De spike-alert van Cloudflare meldt 14.000 WAF-events op de marketingsite in het afgelopen uur, vrijwel allemaal Action: Block. De on-call van vanavond ben jij. Voordat je iets nuttigs kunt doen, moet je weten of dit een echte aanval is, een scanner die een avondje uit heeft, of je eigen WAF-regels die misvuren op een legitieme integratie.

De snelste route door die triage is niet de events lezen. Het is de vorm van de events lezen. Na genoeg van dit soort avonden zie je steeds dezelfde vijf silhouetten in de log opduiken, en bij elk hoort een standaardantwoord: blackholen, throttlen of de schouders ophalen.

Wat een Cloudflare WAF-event eigenlijk bevat

Elk WAF-event dat Cloudflare registreert heeft hetzelfde skelet. Je haalt ze op uit het dashboard onder Security > Events, of je bevraagt ze via de GraphQL Analytics API als je ze in Grafana, Loki of een Slack-kanaal wilt laten landen.

{
  "rayName": "8b3f7e2c1a4d9000",
  "action": "block",
  "ruleId": "100015",
  "source": "waf.managed",
  "clientIP": "45.155.205.233",
  "clientASN": 14061,
  "clientCountry": "US",
  "clientRequestHTTPHost": "abn.company",
  "clientRequestPath": "/.env",
  "clientRequestHTTPMethodName": "GET",
  "userAgent": "Mozilla/5.0 (compatible; Nimbostratus-Bot/v1.3.2)",
  "botScore": 3,
  "edgeResponseStatus": 403,
  "datetime": "2026-06-06T20:14:33Z"
}

Dat is genoeg om vrijwel elk event te classificeren. De velden die ertoe doen voor de triage zijn ruleId, clientASN, clientRequestPath, userAgent en botScore. De rest is voor de post-mortem.

Signatuur 1: één IP loopt de CVE-boodschappenlijst af

Het meest voorkomende patroon in welke WAF-log dan ook. Eén clientIP raakt een reeks paden die niets met jouw site te maken hebben:

GET /.env
GET /.git/config
GET /wp-content/plugins/revslider/temp/update_extract/
GET /actuator/health
GET /phpinfo.php
GET /server-status
GET /.aws/credentials

De user agent is meestal een bekende scannerstring (Nimbostratus, zgrab, Nuclei) of een iets verouderde Chrome. De ASN is vaak een kleine VPS-provider uit de spammerigere hoek. De bot score zit in de enkele cijfers.

Dit is verkenning, geen exploitatie. Ze gaan geen .env vinden op je edge, maar ze gaan ook niet stoppen. Het juiste antwoord is het IP voor een week of twee blackholen op firewall-niveau en doorgaan met je avond.

(ip.src eq 45.155.205.233)  →  Block, expire 14d

Signatuur 2: cloud-ASN, valide browser-UA, één endpoint

Een schone Chrome user agent. Een echte Accept-Language. JavaScript zou uitvoeren als je het vroeg. Het enige wat afwijkt: de request komt van AS24940 (Hetzner), AS16276 (OVH) of AS14061 (DigitalOcean), en hij raakt elke 400ms hetzelfde product- of pricing-endpoint.

Dit is iemand die je site scraped. Kan een concurrent zijn, een price-tracker, een LLM-trainingscrawler die robots.txt negeerde. Ze botweg blokkeren is onbeleefd én zinloos. Ze roteren gewoon door.

Het juiste antwoord is throttlen. Rate-limit op ASN op het dure endpoint, zet de cache-TTL op de response hoger dan het scrape-interval, en laat ze de data hebben tegen een prijs die je je kunt veroorloven.

Takeaway

Als de bot een werkende browser en een echte ASN heeft, is throttlen goedkoper dan blokkeren. Vecht alleen tegen bots die je geld kosten om te serveren.

Signatuur 3: gedistribueerde POSTs op /login

Honderden IPs, elk één of twee requests, allemaal POST richting /login, /wp-login.php, /admin/auth of het token-endpoint van je API. De IPs clusteren niet op ASN. Ze clusteren op residentiële ASN. Ziggo, KPN, Vodafone, Deutsche Telekom. De user agents zien er prima uit.

Dit is credential stuffing via een residentieel proxy-netwerk (Bright Data, IPRoyal, de grijsmarktvarianten). Het signaal zit niet in een enkele request. Het zit in de padconcentratie plus de residentiële ASN-mix plus de afwezigheid van enig ander pad dat opgevraagd wordt.

Blokkeren op IP doet niets. De proxy-pool heeft er een miljoen. Het juiste antwoord is een rate-limiting rule op het auth-endpoint die een managed challenge triggert na, zeg, drie mislukte logins binnen vijf minuten, plus een one-liner custom rule die een JS challenge serveert bij elke POST op dat pad met cf.bot_management.score < 30.

Signatuur 4: je eigen managed rule misvuurt

Het event is geblokkeerd. De rule ID wijst naar iets in de Cloudflare Managed Ruleset, meestal een SQLi- of XSS-patroon uit de OWASP Core Rule Set. Het pad is van jou. De user agent is een normale browser of een gedocumenteerde webhook (Stripe, GitHub, Mailgun). De body, als je hem kunt zien, bevat iets dat er gevaarlijk uitziet maar dat niet is: een JSON-veld met het woord SELECT erin, of een afgesloten apostrof in een klantnaam.

Dit is de false positive. De vervelende. De gebruiker maakt een ticket aan met "ik kan het formulier niet versturen", of je webhook-queue begint vol te lopen.

Het juiste antwoord is de "aanval" laten voor wat hij is en een Skip rule exception schrijven die strak gescoped is op dat pad en die ruleset. Weersta de neiging om de hele managed group uit te zetten. De CRS vangt elke week echte dingen op. Je hoeft alleen jouw ene afwijkende endpoint een vrijbrief te geven.

Waarschuwing

Als je een managed rule globaal uitzet, zet dan een agenda-reminder over 30 dagen om er opnieuw naar te kijken. We zijn bij meer dan één site binnengelopen waar "tijdelijke" exceptions uit 2021 nog altijd niets staan te doen.

Signatuur 5: trage recon vanaf schone roterende IPs

Dit is degene die zich verstopt in een drukke log. Eén request per 30 of 60 seconden. Elk vanaf een ander IP, vaak op een residentiële of mobiele ASN. Elk raakt hetzelfde pad: /admin, /backup.sql, /.env.production, /wp-config.php.bak. Geen user-agent-patroon. Geen burst.

Dit is iemand die over WAF-rate-limits gelezen heeft en er bewust onder blijft. Vannacht vinden ze niets. Morgen zijn ze terug.

IPs blokkeren is zinloos. Het juiste antwoord is het doelwit weghalen. Zet het admin-pad achter een Cloudflare Access-policy of een geofence op land, of geef simpelweg een statische 444 terug voor elke request op dat pad die niet van je kantoor-VPN-range komt. Als ze de deur niet kunnen vinden, kunnen ze ook niet aan de klink rammelen.

De vijf-minuten audit

Open je Cloudflare-dashboard, ga naar Security > Events en voeg vijf opgeslagen filters toe: één per bovenstaande signatuur. Noem ze cve-walk, cloud-asn-scrape, auth-burst, managed-fp, slow-recon. De volgende keer dat de spike-alert om 22:14 afgaat, ben je twee minuten kwijt aan classificeren in plaats van veertig aan lezen.

Toen we de security-event-triage-agent bouwden voor een Nederlandse e-commerce-klant, liepen we tegen het volgende aan: false positives van managed rules verdronken elk echt signaal in de log. Uiteindelijk hebben we WAF-events door een classifier gepijpt die ze in deze vijf patronen indeelde voordat er ook maar iets gepaged werd. Signaturen 1, 3 en 5 gingen daarna naar een on-call mens, 2 en 4 belandden in een wekelijkse digest. Dezelfde vorm werkt als een script van dertig regels tegen de GraphQL Analytics API als je het liever zelf bouwt dan een SOC-tool koopt, of als onderdeel van ons werk rond AI-agents.

Het kleinste wat je vandaag kunt doen: open één van de WAF-spikes van vorige week en probeer elk event in één van de vijf bakken te leggen. Past er iets niet, dan heb je signatuur zes gevonden.

Kern

Lees de vorm van de WAF-log, niet de events. Vijf patronen bepalen of je blackholet, throttlet of de schouders ophaalt. Al het andere is ruis.

FAQ

Hoe krijg ik Cloudflare WAF-events in Slack?

Bevraag de GraphQL Analytics API op een cron van één minuut, classificeer elk event in een bak, en post alleen de samenvatting in Slack. Het ingebouwde webhook-product is te grof voor echte triage.

Moet ik ooit een Cloudflare managed rule volledig uitzetten?

Bijna nooit. Scope een exception op het pad en de ruleset die misvuurt. Zet je een hele groep uit, zet dan een agenda-reminder over 30 dagen of het wordt permanent.

Wat is het praktische verschil tussen een IP blokkeren en challengen?

Block stuurt een 403 en bespaart je de request. Challenge serveert een JS-check die echte browsers passeren. Gebruik challenge voor verkeer van residentiële ASNs, block voor cloud-VPS-scanners.

Is bot score betrouwbaar genoeg om regels op te schrijven?

Op het Bot Management-plan, ja. Onder score 30 is vrijwel altijd automation. Behandel het als één signaal, niet als het enige signaal, en combineer het altijd met een pad- of method-constraint.

securityoperationsautomationai agentsworkflowtooling

Iets bouwen?

Start een project