mod_rewrite jako pierwsza linia obrony przed wstrzyknięciami
Czym jest mod_rewrite?
mod_rewrite jest modułem serwera Apache, przeznaczonym do przepisywania adresów URL. Wykorzystując jego możliwości możemy sprawić by adresy podstron naszego serwisu były bardziej naturalne dla jego użytkowników. Aby skorzystać z jego możliwości należy utworzyć plik „.htaccess” i w nim umieścić reguły przepisywania.
Przykład działania mod_rewrite:
Adres widziany przez użytkowników: http://www.sklep.pl/produkty/szafa.html Adres widziany przez serwer: http://www.sklep.pl/produkty.php?produkt=szafa
Przepisywanie adresów vs wstrzyknięcia
Błędna filtracja danych pochodzących od użytkowników może doprowadzić do modyfikacji kodu strony bądź zapytań skierowanych do bazy danych. W obu przypadkach bezpieczeństwo strony zostaje zagrożone umożliwiając dostęp agresorowi do wrażliwych danych.
Aby agresor mógł wstrzyknąć niebezpieczny kod we wnętrze naszego systemu musi posłużyć się znakami specjalnymi (np. < / > dla ataku XSS, bądź ‚ / = dla SQL Injection). Wywnioskować z tego możemy, że jedynymi znakami względnie bezpiecznymi dla nas są cyfry oraz litery.
Tak więc jeśli mielibyśmy możliwość określenia zakresu znaków wysyłanych przez użytkowników moglibyśmy zwiększyć bezpieczeństwo naszego serwisu. I tu z pomocą przychodzi mod_rewrite.
Case study
Opis
Programista tworzący sklep internetowy spostrzegł, że jego kod podatny jest na atak typu SQL Injection. Mianowicie zmienna „ID” przesyłana wraz z adresem strony trafia do zapytania SQL bez uprzedniej filtracji. Umożliwia to poszerzenie oryginalnego zapytania o kod agresora. Jako, że każda kategoria w sklepie reprezentowana jest przez osobny plik, a kategorii jest tak wiele, że programista nawet nie myśli o ręcznej modyfikacji każdego pliku tworzy regułkę mod_rewrite, która ma uchronić serwis przed wykorzystywaniem słabości zmiennej „ID”.
Rozwiązanie
Kod PHP:
<?php
mysql_connect( 'host', 'user', 'passwd' )
or die( 'Nie udalo sie palczyc z baza danych.' );
mysql_select_db( 'db' )
or die( 'Nie udalo wybrac sie bazy.' );
$query = mysql_query( 'SELECT * FROM produkty WHERE id = ' . $_GET['id'] )
or die( 'Nie udalo sie wykonac zapytania.' );
while( $row = mysql_fetch_row( $query ) )
{
/*
Prezentacja wyników
*/
}
?>
Regułki mod_rewrite:
RewriteEngine on
RewriteCond %{QUERY_STRING} (id=d$)|(id=(d?)&)|^$ [NC]
RewriteRule .* - [L]
RewriteRule .* - [F,L]
Opis rozwiązania
Zadaniem powyższych reguł jest sprawdzenie czy wartość parametru „ID” składa się wyłącznie z cyfr. Jeżeli reguła nie zostanie spełniona zamiast strony produktu wyświetli się strona z błędem 301 czyli brak dostępu do strony.
Przedstawiona reguła pozwala ochronić nasz system w czasie opracowywania bezpieczniejszej wersji skryptu.
Update
Wczorajszego wieczora Łukasz Pilorz poinformował mnie, że powyższa regułka mod_rewrite nie działa. Problemem okazała się rozbieżność w wersjach serwera Apache. Dopiero linia 2.x obsługuje w pełni wyrażenia regularne PERLa, z których skorzystałem pisząc regułkę (d). Aby rewrite zadziałał bez względu na wersję Apache trzeba go zmodyfikować do następującej formy:
RewriteEngine on
RewriteCond %{QUERY_STRING} (id=[0-9] $)|(id=(([0-9] )?)&)|^$ [NC]
RewriteRule .* - [L]
RewriteRule .* - [F,L]
czyli zamienić d na [0-9]
Warto zajrzeć
- Oficjalny opis modułu mod_rewrite (en)
- Kurs wyrażeń regularnych
- Wykorzystanie mod_rewrite do wykrywania ataków XSS
Tagi: mod_rewrite, sql inection
Kategoria: Bezpieczeństwo, Apache
Teraz jeszcze napisz co zrobic gdy nie ma .htaccess obslugi albo mod_rewrite ukradli ;}
Wtedy zostaje edycja plików i dodanie filtrów :-)
Chyba jednak zdecydowana większość programistów zabezpieczenia zawarłaby jż w kodzie…
Ale w sumie spojrzenie na temat ciekawe, takie „z innej strony”
Sposób ten powstał pod podane case study, gdyż jest ono prawdziwe. Miał on zabezpieczyć sklep w czasie wdrażania nowego systemu. Tak więc jest on nie tyle zabezpieczeniem samym w sobie, ale sposobem ochrony podczas wdrażania nowego kodu.
ok, ale przemilczana jest wydajność takiego rozwiązania. mielenie przez apache przy kazdym żądaniu wyrazeń regularnych moze czasem dac znaczący narzut. z drugej strony nie jest to żadna nowość, większe bezpieczeństwo często osiąga się kosztem wydajności – problem jak to wypśrodkować…
Oczywiście, przy większym ruchu może to troszkę przymulić serwer, lecz użycie rewrita ma na celu zyskanie na czasie, który zostanie poświęcony na łatanie kodu, tak więc przez te kilka dni może Apache troche poprawcować ;-)
A nie lepiej po prostu filtrować za pomocą php ?
$get = (int)$_GET[‚zmienna’];
tzw. rzutowanie.
jeśli zmienna nie jest liczbą => skrypt się nie wykona.
Post stary, ale może się komuś przyda :)
Zdecydowanie lepiej. Problem polegał na tym, że zgodnie z case study nie było możliwości edycji plików PHP – a dokładniej była, ale trzeba było wprowadzić szybką łatę, które usunie błąd na czas załatania kodu PHP.