Implementacja Web Application Firewalla w oparciu o Modsecurity dla Nginx

wkontenerach.pl 1 miesiąc temu

Poniższy poradnik opisuje krok po kroku, jak wygląda implementacja web application firewalla opartego na ModSecurity, który od pewnego czasu jest dostępny dla Nginx. Należy pamiętać, iż opisana tutaj procedura działa dla Nginx zainstalowanego bezpośrednio z repozytoriów systemu Ubuntu 20.04.6 LTS. Większość poradników opartych jest na Nginx Plus, skompilowanym Nginx lub Nginx dostępnym z oficjalnych repozytoriów nginx (nie mylić z oficjalnymi repozytoriami systemu).

Poradnik omawia poniższe kwestie:

  • Wdrożenie Web Application Firewall w oparciu o moduł Modsecurity do Nginx instalowany z repozytoriów systemowych.
  • Omówienie kilku przykładowych reguł.
  • Aktualizowanie modułu Modsecurity.

Implementacja Web Application Firewalla – wdrożenie

Zakładam, iż do maszyny, na której jest przeprowadzane wdrożenie, logujemy się na konto użytkownika root.

1. Instalacja wymaganych pakietów:

apt-get install -y apt-utils autoconf automake build-essential git libcurl4-openssl-dev libgeoip-dev liblmdb-dev libpcre++-dev libtool libxml2-dev libyajl-dev pkgconf wget zlib1g-dev

2. Klonowanie repozytorium Modsecurity do katalogu domowego użytkownika root:

cd ~ git clone --depth 1 -b v3/master --single-branch https://github.com/SpiderLabs/ModSecurity

3. Kompilowanie kodu źródłowego z poziomu katalogu Modsecurity (ten krok zajmuje około 20 minut):

cd ModSecurity git submodule init git submodule update ./build.sh ./configure make make install

Należy zignorować pojawiające się ostrzeżenia:
fatal: No names found, cannot describe anything

We wcześniejszym punkcie jest wykonywane tzw. płytkie klonowanie (shallow clone), które pobiera tylko ostatni commit z wybranej gałęzi i nie pobiera pełnej historii i tagów repozytorium. Polecenie “git describe” używane w jednym ze skryptów uruchamianych podczas kompilacji działa na podstawie tagów i historii commitów. W tym wypadku brakuje tych danych, co skutkuje powyższymi ostrzeżeniami.

4. Klonowanie repozytorium ModSecurity Nginx Connector, który będzie użyty jako dynamiczny moduł do Nginxa:

git clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git

5. Weryfikacja wersji aktualnie zainstalowanego Nginxa:

nginx -v

W opisywanym przypadku zwrócona wartość jest następująca:

nginx version: nginx/1.18.0 (Ubuntu)

6. Pobieranie stąd i wypakowywanie wersji Nginx — odpowiedniej dla zainstalowanej na serwerze:

wget https://nginx.org/download/nginx-1.18.0.tar.gz tar zxvf nginx-1.18.0.tar.gz

7. Wejście do folderu z wypakowanym archiwum i kompilacja dynamicznego modułu:

cd nginx-1.18.0 ./configure --with-compat --add-dynamic-module=../ModSecurity-nginx make modules

8. Po zakończeniu procesu kompilacji, w folderze nginx-1.18.0/objs pojawi się następujący plik z rozszerzeniem “.so” → ngx_http_modsecurity_module.so.

9. Kopiowanie pliku ngx_http_modsecurity_module.so do ścieżki /usr/lib/nginx/modules i zmiana jego uprawnień z domyślnych 755 na 644:

cp objs/ngx_http_modsecurity_module.so /usr/lib/nginx/modules cd /usr/lib/nginx/modules chmod 644 ngx_http_modsecurity_module.so

10. Utworzenie w ścieżce /usr/share/nginx/modules-available pliku mod-http-modsecurity.conf z odpowiednią zawartością:

cd /usr/share/nginx/modules-available echo "load_module modules/ngx_http_modsecurity_module.so;" > mod-http-modsecurity.conf

11. Utworzenie w ścieżce /etc/nginx/modules-enabled linku symbolicznego do pliku konfiguracyjnego dostępnego w /usr/share/nginx/modules-available:

ln -s /usr/share/nginx/modules-available/mod-http-modsecurity.conf /etc/nginx/modules-enabled/50-mod-http-modsecurity.conf

12. Utworzenie katalogu modsec w ścieżce /etc/nginx/conf.d i umiejscowienie w nim pliku konfiguracyjnego znajdującego się w repozytorium Modsecurity:

mkdir -p /etc/nginx/conf.d/modsec wget -P /etc/nginx/conf.d/modsec https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended

13. Zmiana nazwy pobranego we wcześniejszym punkcie pliku poprzez usunięcie przyrostka “-recommended”:

mv /etc/nginx/conf.d/modsec/modsecurity.conf-recommended /etc/nginx/conf.d/modsec/modsecurity.conf

14. Kopiowanie pliku unicode.mapping do wcześniej utworzonego katalogu modsec. Plik unicode.mapping w ModSecurity pomaga w wykrywaniu podejrzanych działań przez ujednolicenie znaków z różnych alfabetów i sposobów kodowania. “a” z alfabetu łacińskiego (U+0061) i “а” z cyrylicy (U+0430). Wizualnie są prawie identyczne, ale technicznie to różne znaki.

cp ~/ModSecurity/unicode.mapping /etc/nginx/conf.d/modsec

15. Zmiana domyślnego trybu wykrywania na tryb blokowania:

sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/nginx/conf.d/modsec/modsecurity.conf

16. Użycie zestawu reguł OWASP Coreruleset. Na czas pisania poradnika najnowsza stabilna wersja to 3.3.2. Pobranie i wypakowanie:

wget https://github.com/coreruleset/coreruleset/archive/refs/tags/v3.3.2.tar.gz tar zxvf v3.3.2.tar.gz

17. Przeniesienie katalogu z regułami do głównego katalogu Modsecurity oraz utworzenie pliku crs-setup.conf z domyślnego crs-setup.conf.example:

mv coreruleset-3.3.2/ /etc/nginx/conf.d/modsec/ cd /etc/nginx/conf.d/modsec/coreruleset-3.3.2 cp crs-setup.conf.example crs-setup.conf

18. Wejście do ścieżki /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules i włączenie dwóch reguł poprzez usunięcie przyrostka “.example”:

cd /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules mv REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf.example REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf mv RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf.example RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

19. Dodanie poniższych reguł w pliku /etc/nginx/conf.d/modsec/main.conf. Są to domyślne – wszystkie – reguły przygotowane przez OWASP, które w razie potrzeby należy zmodyfikować. Jest to opisywane na przykładach w dalszej części tekstu.

# OWASP CRS v3 rules # https://coreruleset.org/installation/ Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/crs-setup.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-901-INITIALIZATION.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-903.9002-WORDPRESS-EXCLUSION-RULES.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-905-COMMON-EXCEPTIONS.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-910-IP-REPUTATION.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-911-METHOD-ENFORCEMENT.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-912-DOS-PROTECTION.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-913-SCANNER-DETECTION.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-921-PROTOCOL-ATTACK.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-930-APPLICATION-ATTACK-LFI.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-931-APPLICATION-ATTACK-RFI.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-932-APPLICATION-ATTACK-RCE.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-934-APPLICATION-ATTACK-NODEJS.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-943-APPLICATION-ATTACK-SESSION-FIXATION.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/REQUEST-949-BLOCKING-EVALUATION.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/RESPONSE-950-DATA-LEAKAGES.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/RESPONSE-951-DATA-LEAKAGES-SQL.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/RESPONSE-952-DATA-LEAKAGES-JAVA.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/RESPONSE-953-DATA-LEAKAGES-PHP.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/RESPONSE-954-DATA-LEAKAGES-IIS.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/RESPONSE-959-BLOCKING-EVALUATION.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/RESPONSE-980-CORRELATION.conf Include /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf

20. Utworzenie snippeta w ścieżce /etc/nginx/snippets/waf.conf z zawartością:

modsecurity on; modsecurity_rules_file /etc/nginx/conf.d/modsec/main.conf;

21. Dołączenie utworzonego snippeta w bloku http {} pliku /etc/nginx/nginx.conf:

http { # ... include snippets/waf.conf; }

22. Sprawdzenie konfiguracji i przeładowanie nginxa:

nginx -t systemctl reload nginx.service

23. W pliku loga pojawi się linia podobna do poniższego fragmentu:

2022/10/05 10:52:10 [notice] 4070583#4070583: ModSecurity-nginx v1.0.3 (rules loaded inline/local/remote: 0/728/0)

24. Usunięcie repozytoriów i pobranych plików:

rm -r ~/ModSecurity rm -r ~/ModSecurity-nginx rm -r ~/nginx-1.18.0 rm ~/nginx-1.18.0.tar.gz

Omówienie reguł

Powyższy krok jest ostatnim w kontekście generalnego wdrożenia. Natomiast z pewnością pojawi się wymóg dostosowania reguł web application firewalla do serwera, na którym został wdrożony. Reguły, które mają być brane pod uwagę przed sprawdzaniem poszczególnych zapytań należy umieszczać w pliku REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf. Znajduje się on w ścieżce /etc/nginx/conf.d/modsec/coreruleset-3.3.2/rules. Poniżej kilka przykładów reguł wraz z opisami:

SecRule REMOTE_ADDR "@ipMatch 1.2.3.4" \ "id:1006,\ phase:1,\ pass,\ nolog,\ chain" SecRule REQUEST_URI "@beginsWith /admin/index.php" \ "id:1007,\ ctl:ruleEngine=Off" SecRule REMOTE_ADDR "@pmFromFile /etc/nginx/conf.d/modsec/whitelist_ips.conf" \ "id:1010,\ phase:1,\ nolog,\ allow,\ ctl:ruleEngine=Off" SecRule QUERY_STRING "utm_source|utm_medium|utm_campaign|utm_id|utm_term|utm_content|fbclid|gclid|gad_source|gbraid|wbraid|hgtid|msclkid|ceneo_cid|pfbid|rtgUrl|sznaiid|_gl|_ga|ttclid" \ "id:1016,\ phase:1,\ nolog,\ pass,\ ctl:ruleRemoveById=123456,\ ctl:ruleRemoveById=654321"

Każda reguła zaczyna się od słowa “SecRule”. Reguły można łączyć w łańcuchy reguł zależnych od siebie. Większość atrybutów w definicjach wspomnianych reguł jest dość prosta do zrozumienia, dlatego skupię się na wyjaśnieniu niuansów:

1. Dlaczego w jednej regule jest “allow”, a w innej “pass”?

Po skonfigurowaniu “allow” ModSecurity zakończy przetwarzanie bieżącego zapytania bez dalszego stosowania reguł, co oznacza, iż zostanie ono przepuszczone i wszelkie kolejne reguły zostaną pominięte.

Typowo “allow” stosuje się w regułach dla białych list lub wyjątków, gdzie chcemy upewnić się, iż konkretne zapytania będą automatycznie akceptowane bez dodatkowych kontroli.

Z kolei skonfigurowanie “pass” określa, iż dane zapytanie nie będzie sprawdzane przez reguły określone w “ctl:ruleRemoveById”. Natomiast przez cały czas weryfikacja przez wszystkie pozostałe reguły będzie miała miejsce.

2. Co oznacza “phase”?

Dyrektywa “phase” określa, w którym etapie przetwarzania zapytania HTTP ma zostać wykonana dana reguła. ModSecurity definiuje kilka faz, które pozwalają kontrolować przepływ przetwarzania zapytania od momentu jego odebrania do wysłania odpowiedzi. Poszczególne fazy pozwalają na wykonywanie różnych typów analiz i kontroli na różnych etapach przetwarzania zapytania:

– phase:1 (Request Headers Phase):

Jest to faza analizowania nagłówków zapytania. Reguły w tej fazie mogą sprawdzać nagłówki HTTP jeszcze przed przetworzeniem ciała zapytania. Typowe zastosowanie: filtrowanie na podstawie adresów IP, metod HTTP (np. GET, POST), nagłówków zapytania itp.

– phase:2 (Request Body Phase):

W tej fazie analizowana jest treść (body) zapytania, jeżeli jest dostępna (np. dla żądań POST z danymi formularza lub JSON). Typowe zastosowanie: analiza zawartości formularzy, danych przesyłanych w zapytaniu lub analizy SQL injection, XSS itp.

– phase:3 (Response Headers Phase):

ModSecurity przetwarza nagłówki odpowiedzi serwera, zanim jeszcze zostanie przesłana pełna treść odpowiedzi.Typowe zastosowanie: sprawdzanie nagłówków odpowiedzi, np. Content-Type, aby upewnić się, iż serwer nie wysyła niechcianych treści lub by egzekwować polityki bezpieczeństwa.

– phase:4 (Response Body Phase):

W tej fazie analizowana jest treść odpowiedzi serwera. Typowe zastosowanie: inspekcja zawartości odpowiedzi w celu wykrycia wycieków danych, takich jak numery kart kredytowych, dane osobowe, czy niepożądane treści.

– phase:5 (Logging Phase):

Faza końcowa przeznaczona do logowania. Wszystkie dane są już przetworzone, a celem jest zapisanie logów.

Zazwyczaj nie stosuje się reguł do filtrowania w tej fazie, ponieważ służy ona wyłącznie do rejestracji zdarzeń.

3. Dlaczego raz użyte jest “ctl:ruleRemoveById”, a innym razem “ctl:ruleEngine”?

ctl:ruleRemoveById – usuwa konkretne reguły na podstawie ich ID, umożliwiając bardziej precyzyjne kontrolowanie, które reguły mają być pominięte. W opisywanym przypadku ten atrybut wykluczy stosowne reguły zawarte w silniku web application firewalla dla zapytań mających określone parametry.

ctl:ruleEngine – kontroluje działanie całego silnika reguł ModSecurity, pozwalając na pełne włączenie, wyłączenie lub ograniczenie tylko do detekcji w danym kontekście. Dwie z podanych wyżej przykładowych reguł stosują tę dyrektywę. W jednym przypadku chodzi o białą listę adresów ip wczytywaną z pliku. Natomiast w drugim jest zezwolenie na dostęp do panelu logowania dostępnego pod adresem “/admin/index.php” tylko dla zapytań z adresu IP 1.2.3.4.

Aktualizacja

Co jakiś czas każdy administrator staje przed wymogiem aktualizacji systemu zainstalowanego na danej maszynie. Nie inaczej jest z Nginx. Natomiast, aby nie przerwać przetwarzania przez niego zapytań z powodu błędu niekompatybilności wersji modułu Modsecurity z nową wersją Nginxa, należy najpierw wykonać poniższe kroki:

1. Wykomentowanie zawartości pliku /etc/nginx/snippets/waf.conf. Po skompilowaniu nowej wersji modułu oraz przeprowadzeniu aktualizacji Nginxa należy ponownie odkomentować zawartość wspomnianego pliku.

2. Usunięcie istniejącego pliku modułu ngx_http_modsecurity_module.so ze ścieżki /usr/share/nginx/modules:

cd /usr/share/nginx/modules rm ngx_http_modsecurity_module.so

3. Usunięcie linku symbolicznego 50-mod-http-modsecurity.conf znajdującego się w ścieżce /etc/nginx/modules-enabled:

cd /etc/nginx/modules-enabled rm 50-mod-http-modsecurity.conf

4. W tym momencie należy wykonać kroki 1-11 z pierwszej sekcji niniejszego poradnika.

Podsumowanie i wnioski – Implementacja Web Application Firewalla w oparciu o Modsecurity dla Nginx

Internet jest miejscem, w którym istnieje znaczna ilość złośliwego ruchu. Serwery, będące podstawą niejednego biznesu, muszą być odpowiednio chronione. Jednym z narzędzi, które zapewniają skuteczną ochronę jest Web Application Firewall, który skutecznie został wdrożony dzięki niniejszemu poradnikowi. Oczywiście należy pamiętać, iż reguł może być znacznie więcej. Ruch do danej maszyny musi być analizowany, a reguły dostosowywane w taki sposób by eliminować false positives, czyli przypadki ruchu prawidłowego, ale z pewnych powodów zakwalifikowane jako złośliwe.

By jeszcze skuteczniej zadziałać, można skorzystać z narzędzia fail2ban, które po odpowiedniej konfiguracji filtrów będzie banować na zdefiniowany okres wszystkie wystąpienia blokad przeprowadzonych przez WAF.

Autor

Piotr Bracha — pracuje jako administrator systemów. Karierę zaczynał jako młodszy administrator systemów w call center, gdzie miał m.in. możliwość poznania administrowania systemem CentOS i centralą telefoniczną Asterisk. w tej chwili rozwija się w technologiach kontenerowych i chmurowych. Fan automatyzacji i porządku – w kodzie, ale też w środowisku pracy. Artykuły autorstwa Piotra w języku angielskim znajdziesz w serwisie medium.com.

Idź do oryginalnego materiału