Historia jednej aplikacji

Była sobie firma, która postanowiła “mieć stronę”, skorzystała więc z usług jednej z wielu firemek nazywających się szumnie “agencją interaktywną”. Strona była bardzo prosta, kilka stron informacyjnych i formularz z możliwością wysłania załącznika. Klient był zadowolony, do czasu, gdy na stronie znalazły się nieco inne treści, niż pierwotnie...

Tak, strona zawierała “dodatkową funkcjonalność” (czytaj: błędy), które zostały wykorzystane do podmiany strony. Niestety, z różnych powodów analizy powłamaniowej nie udało się przeprowadzić, nie można było więc stwierdzić, jak intruz dokonał podmiany strony. Oględziny aplikacji (radosny bughunting) pokazał, że intruz mógł zrobić to na co najmniej kilka sposobów. Co gorsza strona “musiała wrócić do sieci”, z przyczyn, które jestem w stanie zrozumieć, ale nie zaakceptować. Jak to zrobić? Teoretycznie powinien błędy usunąć wykonawca, ale nie sądzę, by potrafił, zresztą tchórzliwie odmówił współpracy. Co w takim przypadku?

Na początku co w tej aplikacji było źle. W zasadzie wszystko:

Do tego jeszcze trzeba dodać “strukturę makaronowatą” aplikacji połączoną ze sprawdzonym “wzorcem projektowym copy-paste”, który w tym przypadku polegał na wielokrotnym skopiowaniu tych samych plików (include) i modyfikowaniu w nich kilku linii. Obrazu całości dopełnia jeszcze powszechny listing katalogów, oraz prawa zapisu do wszystkiego (łącznie z plikiem .htaccess) dla wszystkich.

Co można było zrobić (nie modyfikując aplikacji)? Po pierwsze zmienić konfigurację serwera. Na początek zmiana uprawnień do plików, tak, by prawo do zapisu było jedynie tam, gdzie było potrzebne (katalog, do którego były zapisywane przekazywane pliki). Po drugie przeniesienie całej konfiguracji z .htaccess do konfiguracji Apache i zablokowanie możliwości korzystania z plików .htaccess. Usunięte oczywiście zostały zbędne aliasy jak /icons/, /cgi-bin/ czy /manual/, wyłączony został również listing katalogów. Aplikacja osiągnęła “stan wyjścia” do dalszego hardeningu.

Poważnym problemem było umieszczenie plików z danymi oraz plików do includowanych w katalogu, który był obsługiwany przez serwer WWW, kolejnym krokiem było ograniczenie dostępu do plików i katalogów dla użytkowników aplikacji. Założenie było proste – użytkownik potrzebuje dostępu tylko do tych plików, do których może się dostać wykonując prawidłowe operacje na stronach. Lista tych stron była prosta do uzyskania z wykorzystaniem narzędzia typu webspider. Przy konfiguracji zostało zastosowane podejście typu default deny , a następnie tylko dla tych plików i katalogów, które są potrzebne, tworzone były oddzielne reguły w oparciu o Directory, DirectoryMatch, Files i FilesMatch. W rezultacie użytkownik mógł korzystać tylko z tych plików, z których korzystać powinien. Dostęp do innych plików (choćby “kopii zapasowych” plików) był blokowany przez konfigurację Apache. Po potwierdzeniu, że aplikacja nadal działa (funkcjonalnie) i działa zgodnie z założeniami (dostęp tylko do ściśle określonej listy plików) można było przejść do konfiguracji WAF, czyli w tym przypadku mod_security.

Pierwszy etap konfiguracji był prosty – uruchomienie mod_security z zestawem corerules i zweryfikowanie, że aplikacja nadal działa. Kolejnym etapem było zablokowanie w regułach modsecurity kilku szczególnie wrażliwych ścieżek. Choć były one zablokowane już przez konfigurację Apache, to na wszelki wypadek blokował je również modsecurity. Na tym etapie WAF działał w trybie known bad , czyli blokował to, o czym wiadomo, że jest złe.

Ostatnim, najbardziej uciążliwym etapem było stworzenie reguł specyficznych dla aplikacji. Na szczęście dostępny był jej kod źródłowy i dla każdego skryptu można było w prosty sposób uzyskać listę parametrów pobieranych przez niego. Dla każdego pliku (lokalizacji) stworzony został zestaw reguł, który dla każdego z wykorzystywanych parametrów definiował “regułę walidacji”, czyli po prostu regexp , który otrzymana wartość musiała spełniać, by nie zostać odrzucona. Najbardziej problematyczne są oczywiście pola tekstowe, w których często ciężko jest zdefiniować reguły, jakie mają być spełniane. W tym przypadku trzeba było niestety oprzeć się na (złej) zasadzie blacklisty znaków, które są niebezpieczne w danym miejscu aplikacji (przypominam, że był dostęp do kodu źródłowego, więc można było zobaczyć, co się dalej z otrzymanymi danymi dzieje). Na koniec oczywiście kolejny raz testy funkcjonalne oraz weryfikacja, że wszystkie reguły działają tak, jak powinny.

Aplikacja była naprawdę prosta, ale prace nad nią zajęły kilka dni. Jestem praktycznie przekonany, że w tym czasie dobry programista mógł, bez specjalnego trudu, napisać aplikację o analogicznej funkcjonalności, bez błędów oczywiście. Przykład ten pokazuje jednak, że dla bezpieczeństwa aplikacji konfiguracja serwera nie jest bez znaczenia. Dodatkowo nawet bardzo dziurawe aplikacje mogą być “załatane” z wykorzystaniem rozwiązań typu WAF.

UWAGA (dopisek 13:05): Bynajmniej nie oznacza to, że można pisać/odbierać dziurawe aplikacje i liczyć, że WAF (czy inny trzyliterowy skrót) to załatwi. To tak na wypadek, gdyby ktoś miał wątpliwości w tym temacie. Natomiast sensownie skonfigurowany WAF pod konkretną aplikację ma sens, jako kolejna warstwa zabezpieczeń. W opisywanej historii, z uwagi na “radosną twórczość programisty”, WAF był w zasadzie jedyną warstwą zabezpieczeń.

Oryginał tego wpisu dostępny jest pod adresem Historia jednej aplikacji

Autor: Paweł Goleń