ModSecurity med regeluppsättningen OWASP CRS slås på på tio minuter och stängs av tre dagar senare — efter att artiklar slutat sparas i administrationsgränssnittet, filuppladdningen gått sönder och kunden inte kunnat lägga en beställning för att adressen råkade innehålla en apostrof. Slutsatsen ”WAF står i vägen” ligger nära till hands, men den är fel: nästan alla dessa blockeringar löses med tre eller fyra riktade undantag, och hela saken handlar om att hitta dem på rätt sätt.

Slå inte på blockering direkt

Första veckan är enbart observation. I /etc/modsecurity/modsecurity.conf:

SecRuleEngine DetectionOnly

I det läget skriver WAF ner allt den skulle ha blockerat men blockerar ingenting. En vecka verklig trafik — inklusive ditt eget arbete i administrationsgränssnittet, bilduppladdning och en beställning — ger listan över faktiska falska larm i stället för hypotetiska. Att växla till On är meningsfullt först när den listan är genomgången.

Så fungerar CRS: inte en regel, utan en summa

Nyckeln till allt som följer. CRS blockerar nästan aldrig en förfrågan med en enda regel. Varje regel som utlöses lägger anomalipoäng till förfrågan, och blockeringen sker när summan överstiger tröskeln. Därför ser du inte en rad i loggen utan flera, och den sista är regeln med id 949110 — den som summerar.

Praktisk slutsats: undantaget ska göras för regeln som gav poängen, inte för 949110. Stänger du av den summerande regeln stänger du av hela uppsättningen, och WAF finns bara kvar som en rad i konfigurationen.

Nästa följd är paranoianivån. Som standard är den ett, och det är rätt val. Nivå 2 och 3 lägger till regler som med säkerhet ger falska larm på vanliga sajter, och de bör slås på först när nivå ett är helt intrimmad.

Hitta den skyldiga regeln

Allt du behöver finns i granskningsloggen (/var/log/modsec_audit.log) och i webbserverns fellogg. Vi söker på blockeringstiden:

sudo grep -o 'id "[0-9]*"' /var/log/modsec_audit.log | sort | uniq -c | sort -rn | head

Det är en frekvenslista över utlösta regler. Därefter tittar vi på vad ett visst id faktiskt utlöstes av:

sudo grep -A5 'id "942100"' /var/log/modsec_audit.log | head -40

Tre saker behövs: regelns id, parameterns namn (ARGS:content, ARGS:comment) och förfrågans sökväg. Av dem byggs undantaget.

De vanliga misstänkta

Listan upprepar sig från sajt till sajt:

  • 942100 — SQL-injektion. Utlöses på textfält med långt innehåll: artikeltext, produktbeskrivning, kommentar. Apostrofer, parenteser och ord som select ser misstänkta ut för detektorn även i vanlig text;
  • 941100 och 941xxx — XSS. Kommer tillsammans med en visuell redigerare: HTML-taggar i fältet är hela dess arbetssätt;
  • 920420 — otillåten Content-Type. Bryter API:er och filuppladdning: standarduppsättningen tillåtna typer är smal och application/json ingick inte i äldre versioner;
  • 913100 — skanner enligt User-Agent. Fångar legitima verktyg tillsammans med skannrarna: tillgänglighetsövervakning, curl i dina egna skript;
  • 200002, 200004 — fel vid tolkning av förfrågans kropp. Betyder oftast inte ett angrepp utan en överskriden storleksgräns, alltså uppladdning av en stor fil.

Tre sätt att göra undantag

I stigande grovhet. Alla skrivs i en egen fil (till exempel /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES.conf) och inte i CRS egna filer: regeluppsättningen uppdateras, och dina ändringar försvinner vid uppdateringen.

Ta bort en parameter från en regel. Det mest precisa alternativet, och det vi strävar efter:

SecRuleUpdateTargetById 942100 "!ARGS:content"

Stäng av regeln enbart på en sökväg. Passar när en bestämd sida bullrar — redigeraren, importen, omdömesformuläret:

SecRule REQUEST_URI "@beginsWith /admin/post" \
    "id:1001,phase:1,pass,nolog,ctl:ruleRemoveById=942100"

Stäng av regeln helt. Sista utvägen och nästan alltid ett tecken på att orsaken inte hittats:

SecRuleRemoveById 942100

Skillnaden mellan det första och det tredje är betydande. I första fallet slutar fältet ”artikeltext” kontrolleras mot SQL-injektioner av en regel; i andra och tredje fallet slutar hela sajten kontrolleras. Skillnaden i arbetsinsats är omkring fem minuter.

Efter ändringarna — konfigurationskontroll och mjuk omladdning:

sudo apachectl configtest && sudo systemctl reload apache2
sudo nginx -t && sudo systemctl reload nginx

Ordningen som sparar en vecka

  1. En vecka i DetectionOnly med vanligt arbete på sajten, inklusive administrationsgränssnittet och uppladdningar.
  2. Frekvenslistan över regler ur granskningsloggen. Gå uppifrån och ned — de tre första ger nittio procent av bullret.
  3. För var och en: ta reda på vilken parameter och vilken sida det gäller. Gör undantaget per parameter, inte per regel.
  4. Först nu SecRuleEngine On.
  5. Titta på blockeringarna en gång i månaden: när sajten ändras dyker nya falska larm upp.

Den sista punkten är det som allt vanligen strandar på: granskningsloggen är gigabyte av text och ingen läser den för hand. Poängen med en samlad panel är att listan över utlösta regler, blockerade förfrågningar och den aktiva uppsättningen ligger framför ögonen i stället för att grävas fram med grep. Hur det ser ut visar demonstrationssidan nedan.