ModSecurity z zestawem reguł OWASP CRS włącza się w dziesięć minut, a wyłącza trzy dni później — po tym, jak artykuły przestają się zapisywać w panelu, psuje się wysyłanie plików, a klient nie może złożyć zamówienia, bo w adresie znalazł się apostrof. Wniosek „WAF przeszkadza w pracy” nasuwa się sam i jest błędny: niemal wszystkie te blokady da się usunąć trzema–czterema precyzyjnymi wyjątkami, a cała rzecz polega na tym, żeby je poprawnie znaleźć.
Nie włączać blokowania od razu
Pierwszy tydzień to sama obserwacja. W /etc/modsecurity/modsecurity.conf:
SecRuleEngine DetectionOnly
W tym trybie WAF zapisuje wszystko, co by zablokował, i nie blokuje niczego. Tydzień prawdziwego ruchu — łącznie z własną pracą w panelu, wysyłaniem obrazków i złożeniem zamówienia — daje listę rzeczywistych fałszywych trafień zamiast hipotetycznych. Przejście na On ma sens dopiero wtedy, gdy ta lista zostanie przerobiona.
Jak działa CRS: nie jedna reguła, lecz suma
To klucz do wszystkiego dalszego. CRS prawie nigdy nie blokuje żądania jedną regułą. Każda reguła, która zadziała, dodaje punkty anomalii, a blokada następuje, gdy suma przekroczy próg. Dlatego w dzienniku widać nie jeden wiersz, lecz kilka, a ostatnim będzie reguła o identyfikatorze 949110 — ta, która podsumowuje.
Praktyczny wniosek: wyjątek trzeba zrobić dla reguły, która naliczyła punkty, a nie dla 949110. Wyłączając regułę sumującą, wyłącza się cały zestaw, a z WAF-a zostaje tylko wiersz w pliku konfiguracyjnym.
Drugi wniosek dotyczy poziomu paranoi. Domyślnie wynosi jeden i to właściwy wybór. Poziomy 2 i 3 dodają reguły, które z założenia dają fałszywe trafienia na zwykłych witrynach; włączać je warto dopiero wtedy, gdy poziom pierwszy jest w pełni dostrojony.
Znaleźć winną regułę
Wszystko potrzebne jest w dzienniku audytu (/var/log/modsec_audit.log) i w dzienniku błędów serwera WWW. Szukamy po czasie blokady:
sudo grep -o 'id "[0-9]*"' /var/log/modsec_audit.log | sort | uniq -c | sort -rn | head
To lista częstotliwości reguł, które zadziałały. Dalej, dla konkretnego identyfikatora, sprawdzamy, na czym dokładnie zadziałała:
sudo grep -A5 'id "942100"' /var/log/modsec_audit.log | head -40
Potrzebne są trzy rzeczy: identyfikator reguły, nazwa parametru (ARGS:content, ARGS:comment) i ścieżka żądania. Z nich buduje się wyjątek.
Zwykli podejrzani
Lista powtarza się od witryny do witryny:
- 942100 — wstrzyknięcie SQL. Reaguje na pola tekstowe o długiej treści: treść artykułu, opis produktu, komentarz. Cudzysłowy, nawiasy i słowa w rodzaju
selectwyglądają dla detektora podejrzanie nawet w zwykłej prozie; - 941100 i rodzina 941xxx — XSS. Przychodzą razem z edytorem wizualnym: znaczniki HTML w polu to właśnie sens jego pracy;
- 920420 — niedozwolony Content-Type. Psuje interfejsy API i wysyłanie plików: domyślny zestaw dozwolonych typów jest wąski, a w starszych wersjach nie było w nim
application/json; - 913100 — skaner po nagłówku User-Agent. Razem ze skanerami łapie legalne narzędzia: monitoring dostępności, curl we własnych skryptach;
- 200002, 200004 — błędy analizy treści żądania. Zwykle oznaczają nie atak, lecz przekroczony limit rozmiaru, czyli wysyłanie dużego pliku.
Trzy sposoby na wyjątek
Według rosnącej brutalności. Wszystkie trafiają do własnego pliku (na przykład /etc/modsecurity/crs/REQUEST-900-EXCLUSION-RULES.conf), a nie do plików samego CRS: zestaw reguł jest aktualizowany i zmiany zniknęłyby razem z nim.
Wyjąć jeden parametr spod jednej reguły. Najdokładniejszy wariant i ten, do którego dążymy:
SecRuleUpdateTargetById 942100 "!ARGS:content"
Wyłączyć regułę tylko na jednej ścieżce. Pasuje, gdy hałasuje konkretna strona — edytor, import, formularz opinii:
SecRule REQUEST_URI "@beginsWith /admin/post" \
"id:1001,phase:1,pass,nolog,ctl:ruleRemoveById=942100"
Wyłączyć regułę całkowicie. Ostateczność i prawie zawsze znak, że przyczyny nie znaleziono:
SecRuleRemoveById 942100
Różnica między pierwszym a trzecim jest istotna. W pierwszym przypadku jedno pole — tekst artykułu — przestaje być sprawdzane pod kątem wstrzyknięcia SQL przez jedną regułę; w drugim i trzecim przestaje być sprawdzana cała witryna. Różnica w nakładzie pracy to jakieś pięć minut.
Po zmianach sprawdzamy konfigurację i przeładowujemy łagodnie:
sudo apachectl configtest && sudo systemctl reload apache2
sudo nginx -t && sudo systemctl reload nginx
Kolejność, która oszczędza tydzień
- Tydzień w
DetectionOnlyze zwykłą pracą na witrynie, łącznie z panelem i wysyłaniem plików. - Lista częstotliwości reguł z dziennika audytu. Przerabiać od góry — pierwsze trzy–cztery dadzą dziewięćdziesiąt procent szumu.
- Przy każdej: ustalić, jaki parametr i na jakiej stronie. Wyjątek robi się według parametru, a nie według reguły.
- Dopiero teraz
SecRuleEngine On. - Raz w miesiącu zajrzeć do blokad: witryna się zmieniła, więc pojawiły się nowe fałszywe trafienia.
Właśnie na tym ostatnim punkcie wszystko zwykle się rozbija: dziennik audytu to gigabajty tekstu, których nikt nie będzie czytał ręcznie. Sens zebranego panelu polega na tym, żeby lista reguł, które zadziałały, zablokowane żądania i aktywny zestaw były przed oczami, a nie wydobywane grepem. Jak to wygląda, pokazuje strona demonstracyjna poniżej.