HTTP Cache Poisoning via Host Header Injection
Napisał: Patryk Krawaczyński
27/06/2013 w Ataki Internetowe, Bezpieczeństwo Brak komentarzy. (artykuł nr 417, ilość słów: 745)
P
owszechną praktyką wśród programistów piszących własne webaplikacje oraz webowych frameworków odkrywających koło od nowa jest poleganie na wartościach zwracanych w nagłówku HTTP Host. Jest to bardzo wygodny sposób na gwarancję, że ta sama aplikacja zostanie uruchomiona na localhoście, serwerach środowiska: developerskiego, testowego, produkcyjnego, innych domenach i subdomenach itd., bez wprowadzania modyfikacji w kod aplikacji. Prosty przykład w PHP:
Link do logowania:
<a href="<?php echo $_SERVER['HTTP_HOST'] ?>/logowanie">Login</a>
Link do stylów CSS:
<link rel="stylesheet" type="text/css" href="<?php echo $_SERVER['HTTP_HOST'] ?>/st.css">
Link do skryptów JS:
<script src="<?php echo $_SERVER['HTTP_HOST'] ?>/jquery.js?v=1.2.3">
Okazuje się, że to bardzo zły pomysł – niezależnie od języka programowania. Wartość nagłówka Host
w protokole HTTP jest dowolnym tekstem kontrolowanym i przekazywanym przez klienta, ale czasami traktuje się go tak, jakby był bezpieczną zmienną – a nią nie jest. Za przykład może tutaj posłużyć mechanizm HTTP cache umieszczony gdzieś po drodze pomiędzy webaplikacją, a użytkownikami. Może to być dowolne proxy typu cache zainstalowane na tym samym serwerze WWW, co aplikacja lub odseparowany CDN (ang. Content Delivery Network), którego zadaniem jest także rozkładanie ruchu i buforowanie zapytań. Jeśli konfiguracja samej aplikacji jest nieprawidłowa, a także serwera WWW i akceleratora HTTP – atakujący jest w stanie wstrzyknąć w taki system naczyń połączonych dowolny nagłówek HTTP typu Host i przepisać adresy na każdej stronie tak, aby kierowały do jego spreparowanych zasobów (np. szkodliwego oprogramowania). Tego typu akcja nie będzie jednorazowa ponieważ mechanizm cache – mający określony czas wygaśnięcia obiektów – będzie przez jakiś czas zwracał użytkownikom podrobione adresy. Po jego wygaśnięciu i odświeżeniu – atakujący może ponownie dokonać wstrzyknięcia nagłówka – i tak cyklicznie przedłużać atak. Przykładowy scenariusz takiego ataku:
$ telnet startup.com 80 Trying 1.2.3.4... Connected to startup.com. Escape character is '^]'. GET /index.html HTTP/1.1 User-Agent: X-Agresor Host: attacker.com HTTP/1.1 200 OK Date: Wed, 10 Jun 2013 00:27:45 GMT Server: Apache Cache-Control: max-age=9600 Expires: Wed, 17 Jun 2014 00:27:45 GMT Content-Length: 2959 Content-Type: text/html; charset=utf-8 <html> <head> <title>StartUP</title> </head> <body> <a href="http://attacker.com/logowanie">Login</a> <link rel="stylesheet" type="text/css" href="http://attacker.com/st.css"> <script src="http://attacker.com/jquery.js?v=1.2.3"> [... dalszy kod strony...] </body> </html>
Tak spreparowana strona (nagłówek Host: startup.com został zamieniony na Host: attacker.com) zostanie wstrzyknięta do pamięci cache i serwowana użytkownikom. Istnieje jeszcze duże prawdopodobieństwo, że dzięki nagłówkom takim jak: Cache-Control oraz Expires zostanie ona dodatkowo zapisana w pamięci cache przeglądarki użytkownika.
Jak zabezpieczyć się przed tego typu sytuacją? Zaczynając od naszej webaplikacji – jeśli koniecznie chcemy korzystać z dynamicznie dostarczanego nagłówka – powinna powstać lista dozwolonych jego wartości, która za pomocą walidacji by dopuszczała tylko prawidłowe adresy (również ich składnie). Przykładem może być mechanizm host header validation użyty w frameworku Django. Niektóre webaplikacje zamiast zmiennej $_SERVER['HTTP_HOST']
wykorzystują jako zamiennik $_SERVER['SERVER_NAME']
, co teoretycznie jest bezpieczniejsze, jeśli użyjemy odpowiedniej konfiguracji serwera WWW – w przypadku serwera Apache chodzi o włączenie opcji UseCanonicalName, która czyni wartość SERVER_NAME bardziej zaufaną. W serwerze Nginx osiągniemy to określając dokładną (nie wildcardową) wartość server_name. Bardzo dobrym pomysłem jest również używanie hostów wirtualnych typu catch-all. Są to vhosty, do których odwołują się serwery WWW, jeśli w żądaniu klienta pojawi się nierozpoznany / niezdefiniowany nagłówek typu Host. Powinny być one pierwszym zdefiniowanym vhostem w naszym serwerze (instrukcje dla serwera Apache oraz Nginx). Analogiczną walidację dozwolonych nagłówków typu host powinniśmy wprowadzić dla używanej warstwy cache. Na przykład w serwerze Varnish mógłby to być zapis:
if (req.http.host !~ "^(?i)startup.com$") { error 500; }
Zatruwanie pamięci cache jest tylko jednym z przykładów, jakie daje możliwość wstrzyknięcia tego nagłówka HTTP. W zależności od mechanizmów webaplikacji, które są podatne na tego rodzaju atak możliwe jest również przeprowadzenie takich ataków jak: Cross-Site Scripting (XSS), HTTP: Response Smuggling, Splitting oraz Request Smuggling, czy File Download Injection.
Więcej informacji: HTTP Cache Poisoning via Host Header Injection, Practical HTTP Host header attacks, Divide and Conquer – HTTP Response Splitting, Web Cache Poisoning Attacks, and Related Topics, What About HTTP Header Injection?