Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book
Jak w prosty sposób zwiększyć bezpieczeństwo aplikacji webowej
W niniejszym artykule przedstawiamy kilka podstawowych zasad, których stosowanie pozwala niskim kosztem zwiększyć ogólny poziom bezpieczeństwa aplikacji webowej.
Odpowiedni doctype
Każda strona HTML powinna zaczynać się od zdefiniowania typu dokumentu (document type/doctype), dzięki czemu przeglądarka wie, w jaki sposób parsować dany dokument. Jeszcze kilka lat temu te deklaracje wyglądały jak „potworki”, np. <!DOCTYPE html PUBLIC „-//W3C//DTD XHTML 1.0 Transitional//EN” „http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> ; na szczęście standard HTML5 znacząco uprościł składnię i teraz wystarczy zdefiniować <!DOCTYPE html>.
Jeżeli na stronie nie zostanie zdefiniowany żaden doctype, przeglądarki internetowe będą próbowały go zgadywać na podstawie dalszej treści dokumentu. W najgorszym wypadku, może okazać się, że dokument zostanie wyrenderowany w tzw. „quirks mode”, którego celem jest zachowanie kompatybilności wstecznej z wczesnymi czasami sieci web, w których wiele technologii nie było jeszcze ustandaryzowanych. W tym trybie przeglądarki obsługują całą masę starych i przestarzałych konstrukcji składniowych, które mogą prowadzić do nieoczekiwanych XSS-ów lub wycieków danych innego typu. Jednym z bardziej znanych przykładów wykorzystania „quirks mode” do wykonania XSS-a jest użycie go do ominięcia ochrony anty-XSS z frameworka .NET. Linkowany artykuł odnosi się do Internet Explorera 9, jednak nawet w najnowszych wersjach IE nadal możliwe jest wymuszenie „quirks mode” w pewnych warunkach.
Podsumowując: na każdej stronie HTML należy zdefiniować doctype w postaci: <!DOCTYPE html>. Dzięki temu mamy gwarancję, że dokument nie zostanie nigdy wyrenderowany w „quirks mode”.
Strict-Transport-Security
Jeśli wdrażamy webaplikację, która używana jest w protokole HTTPS i nie powinna nigdy być uruchamiana w HTTP, należy rozważyć wprowadzenie nagłówka HTTP Strict-Transport-Security (HSTS). Jeżeli dla danej domeny zdefiniujemy HSTS, zapewniamy sobie dwa dodatkowe zabezpieczenia przed atakami typu man-in-the-middle:
- Mamy gwarancję, że w trakcie obowiązywania dyrektywy HSTS, przeglądarka nigdy nie wykona zapytania używając nieszyfrowanego protokołu HTTP,
- Jeżeli próba negocjacji połączenia SSL zaowocuje błędem – np. niepoprawnym lub nieważnym certyfikatem), przeglądarka nie pozwoli użytkownikowi na zaakceptowanie takiego certyfikatu..
W definicji nagłówka HSTS wymagany jest parametr max-age określający w sekundach czas ważności dla tego nagłówka. Po upływie tego czasu, nagłówek przestanie obowiązywać i wykonywanie zapytań HTTP dla danej domeny będzie znów możliwe.
Przykład: aby zdefiniować HSTS z ważnością na rok, należy dodawać w odpowiedziach HTTP dodatkowy nagłówek:
Strict-Transport-Security: max-age=31536000
Subresource Integrity (SRI)
Niezależnie od swojego poziomu złożoności, spora część aplikacji webowych opiera się na zewnętrznych bibliotekach lub frameworkach – takich jak jQuery, Angular, KnockoutJS itp. W celu zmniejszenia ruchu sieciowego na swoich serwerach, często korzysta się z CDN-ów (Content Delivery Network), z których skrypt jest pobierany przez przeglądarkę. Przykładowo: jQuery możemy pobrać bezpośrednio z serwera autorów:
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
Zauważmy, że w ten sposób dajemy zewnętrznemu skryptowi pełny dostęp do drzewa DOM naszej strony. Jeżeli komuś uda się włamać na serwer code.jquery.com i podmienić plik z jQuery (na przykład wrzucając tam exploity na przeglądarki), wówczas i my oberwiemy rykoszetem.
Aby zabezpieczyć się przed takim ryzykiem, wprowadzono mechanizm Subresource Integrity (SRI). SRI pozwala dodać do tagów <script> lub <link> dodatkowy atrybut integrity, w którym podaje się enkodowany w Base64 hash SHA-256, SHA-384 lub SHA-512 dla zewnętrznego pliku. Jeżeli hash z atrybutu nie zgadza się z hashem pobranego pliku, przeglądarka nie wykona takiego pliku.
Aby więc skorzystać z dobrodziejstw SRI, wcześniej podany przykład z jQuery musimy zapisać następująco:
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha384-rY/jv8mMhqDabXSo+UCggqKtdmBfd3qC2/KvyTDNQ6PcUJXaxK1tMepoQda4g5vB"></script>
Aby obliczyć właściwą wartość atrybutu integrity dla danego pliku, możemy posłużyć się stroną: https://www.srihash.org/.
Flagi HttpOnly/Secure
Do ciasteczek HTTP możemy dopisać dwie flagi zwiększające ich bezpieczeństwo: HttpOnly oraz Secure.
Flaga HttpOnly pozwala uchronić się przed jednym z najpowszechniejszych skutków ataków XSS, tj. wykradzeniem ciastka sesyjnego (np. JSESSIONID lub PHPSESSID). Typowo atak polegał na tym, że napastnik wyciągał w JavaScripcie zawartość zmiennej document.cookie, którą wysyłał na swój serwer, by następnie przejąć sesję użytkownika. Ciasteczko oznaczone flagą HttpOnly będzie wysyłane w zapytaniach do serwera, nie pojawia się jednak w document.cookie.
Flaga Secure, z kolei przydaje się, gdy mamy aplikację, która może zadziałać zarówno w HTTP, jak i HTTPS. Dzięki niej mamy gwarancję, że przeglądarka nie wyśle danego ciasteczka w ramach połączenia HTTP, a tylko i wyłącznie HTTPS.
Przykładowy nagłówek Set-Cookie z ustawieniem obu flag wygląda następująco:
Set-Cookie: PHPSESSID=dddjg49acm041jd0as; HttpOnly; Secure
Sposób ustawienia tych flag na ciastka sesyjne (jak również ewentualnie na inne ciastka) zależy od konkretnej używanej technologii. Stosowna instrukcja powinna się znaleźć w dokumentacji.
Nagłówek X-XSS-Protection
Przeglądarki Chrome oraz Internet Explorer posiadają wbudowany filtr chroniący przed atakami Reflected XSS. Działanie tych filtrów jest koncepcyjnie bardzo proste: sprawdzają one, czy w zapytaniu HTTP pojawiają się tagi HTML-owe lub inne fragmenty kodu JS – oraz czy później ten sam kod znajduje się w odpowiedzi. Jeżeli tak jest, to uznają, że mają do czynienia z atakiem XSS i blokują ten fragment kodu. Innymi słowy, jeżeli dla zapytania pod adres: http://example.com/<img+src=1+onerror=alert(1)>, w odpowiedzi pojawi się gdzieś tag <img src=1 onerror=alert(1)>, to przeglądarki nie dopuszczą do jego wykonania.
Okazuje się jednak, że takie zachowanie filtrów XSS-owych może pozwolić na wykonanie XSS-a w sytuacji, w której inaczej nie było to możliwe! Polecam przejrzeć prezentację japońskiego badacza bezpieczeństwa przeglądarek – Masato Kinugawy, pt. X-XSS-Nightmare.
Zarówno w IE, jak i Chromie istnieje możliwość włączenia innego trybu filtru XSS dzięki nagłówkowi X-XSS-Protection. Poniżej opis możliwych zawartości:
- X-XSS-Protection: 0 – filtr XSS-owy jest wyłączony; przeglądarka nie próbuje wykrywać ataków XSS,
- X-XSS-Protection: 1 – opcja domyślna; filtr XSS-owy jest włączony w trybie, w którym blokowany jest tylko fragment kodu podejrzewany o bycie payloadem XSS-owym,
- X-XSS-Protection: 1; mode=block – filtr XSS-owy jest włączony w trybie, w którym wykrycie próby XSS-a skutkuje przerwaniem renderowania strony (wyświetlana jest tylko biała strona).
Najbardziej zalecanym trybem pracy jest ten ostatni: pozwala blokować ataki XSS, a zarazem uniknąć ryzyk omówionych m.in. we wspomnianej prezentacji X-XSS-Nightmare.
Nagłówek X-Content-Type-Options
Nagłówek X-Content-Type-Options przyjmuje tylko jedną możliwą wartość:
X-Content-Type-Options: nosniff
Wyłącza on zgadywanie typu MIME strony przez przeglądarki, pozwala również ochronić się przed atakami polegającymi na dołączaniu plików w innym kontekście niż wskazuje na to ich Content-Type. Przykład: mamy aplikację webową, w której użytkownik może wgrywać obrazki. W innym miejscu tej aplikacji ma ograniczoną kontrolę nad plikiem JavaScript, który jest wykonywany – w związku z czym próbuje wykonać następujący atak:
<script src="http://example.com/uploads/image.png”></script>
Jeżeli użyjemy nagłówka X-Content-Type-Options, takie wykonanie skryptu nie powiedzie się jeżeli Content-type odpowiedzi będzie równy np. image/png. Przeglądarka wyświetli tylko w konsoli błąd, że typ MIME image/png nie pasuje do kontekstu skryptowego.
Nagłówek X-Frame-Options
Nagłówek X-Frame-Options (XFO) został wprowadzony jako odpowiedź na ataki Clickjacking, choć przydaje się również przy ochronie przed innymi, bardziej subtelnymi atakami. Krótko mówiąc, nagłówek ten pozwala ograniczyć listę domen w jakich nasza webaplikacja może być umieszczana w tagach <frame>, <iframe> oraz <object>. Przyjmuje następujące wartości:
- X-Frame-Options: Deny – strona nie może być umieszczana w ramce na żadnej innej podstronie,
- X-Frame-Options: SameOrigin – strona może być umieszczana w ramce tylko jeżeli główna ramka jest w tej samej domenie (tj. ramka widoczna w pasku adresu)
- X-Frame-Optoins: Allow-From http://example.com – strona może być umieszczana w ramce tylko na wskazanej domenie.
Jeżeli nie planujemy na naszej stronie udostępniać widgetów, które muszą być ramkowane na innych stronach, zaleca się ustawić nagłówek XFO z wartością SameOrigin lub Deny.
Podsumowanie
W artykule przedstawiono kilka podstawowych sposobów na zwiększenie bezpieczeństwa aplikacji webowej. Nie wyczerpuje on jednak całkowicie tematu. Między innymi, na Sekuraku opisywaliśmy nagłówek Public-Key-Pins; warto również zainteresować się nagłówkiem Content-Security-Policy, który pozwala precyzyjnie określić polityki ładowania zasobów dla danej aplikacji.
— Michał Bentkowski, pentester w Securitum
Super artykuł!
Michał, a może jeszcze paragraf o HTTP Access Control (CORS)? Chyba dobrze by pasował w tym towarzystwie :)
Dla ciasteczek można tez ustawić flagę SameSite: https://www.igvita.com/2016/08/26/stop-cross-site-timing-attacks-with-samesite-cookies/
Od razu można też wspomnieć o dwóch nowych nagłówkach, które właśnie są standaryzowane: https://chloe.re/2016/08/19/more-control-with-clear-site-data-and-feature-policy/
No to są już 4 paragrafy do dopisania ;) W sumie dobry poradnik by się z tego mógł zrobić.
Czyli strona, na której obecnie się znajdujemy jest podatna na Clickjacking :)