Bezpieczeństwo aplikacji – atak typu XSS

Nie dawniej jak wczoraj otrzymałem od Klienta e-mail mówiący o tym, że moja aplikacja jest podatna na ataki XSS. Czytając to miałem oczy jak pięciozłotówki. W mojej głowie w kółko zadawałem sobie pytanie – ja, stary wyjadacz, przepuściłem kod podatny na XSS? Jak to w ogóle możliwe? A jednak… Chwila nieuwagi, jeden słabszy dzień w pracy i stało się…

opis ataku znajduje się na oficjalnej stronie OWASP: https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)

Mój przypadek

Ostatnio wdrożyłem aplikację w 14 różnych krajach. Jak się domyślasz, jeden wspólny kod i wiele krajów, to musi być skomplikowane. Generalnie aplikacja jest mocno konfigurowalna. Jedną z możliwości jest ustawienie formatera adresu specyficznego dla danego kraju (np.: inna maska dla kodu pocztowego, czy też numer ulicy przed nazwą ulicy, itd.). Dodatkowym wymaganiem Klienta było, by taki adres ładnie wyświetlić, coś pogrubić, wstawić div’a, itp.

No to implementujemy…

W zespole postanowiliśmy, że formater adresu to będzie nic innego jak Java Bean, który może być wczytywany w zależności od zdefiniowanego propertiesa. Formater wypluwał z siebie kod HTML, który był wyświetlany poprzez atrybuty Thymeleaf: <span th:utext=””>. (dla osób nieznających Thymeleaf – utext renderuje content bez escape’owania znaków). Jak nietrudno zauważyć, w klasyczny sposób otworzyliśmy się na wstrzyknięcie danych z formularza zmiany adresu. W polu nazwa ulicy wystarczyło wpisać prosty kod JavaScript <script>alert(‚Hello World’);</script> i nasza przeglądarka wyświetlała piękny alert. 

Jak to poprawić?

W trybie pilnym zajęliśmy się poprawą bezpieczeństwa aplikacji. Tylko teraz pytanie, jak to ogarnąć? W naszym przypadku w całej aplikacji nie spodziewamy się HTML’a pochodzącego od użytkownika. Są to proste formularze, w których można podać swój adres, imię i nazwisko, daty, itd. Specyfika naszej aplikacji mówi nam, że istnieją co najmniej 3 różne podejścia do załatania tej dziury bezpieczeństwa:

  1. Zmiana na widoku. Przejrzeć szablony HTML i sprawdzić w ilu miejscach renderujemy dane używając utext lub wypisujemy je bezpośrednio. Następnie zmienić implementację escape’ując znaki.
    Poważny minus tego rozwiązania to fakt, że niebezpieczne dane ciągle mogą być przechowywane w bazie w czystej postaci (czyli HTML).
  2. Walidacja danych wejściowych, czyli sprawdzenie, czy podczas wysyłania formularza nie przychodzi nam HTML w zbindowanym obiekcie form. Można do tego użyć adnotacji @SafeHtml pochodzącej z pakietu org.hibernate.validator.constraints. W momencie, gdy użytkownik wprowadzi kod HTML, zostanie rzucony odpowiedni wyjątek.
  3. Wycięcie kodu HTML z danych wejściowych. Prosty, a zarazem skuteczny sposób, by zabezpieczyć aplikację (przede wszystkim bazę danych) przed niespodziewanym kodem HTML. W naszej Springowej aplikacji było to proste – wystarczyło zarejestrować swój własny PropertyEditor, który odpowiada za bindowanie pól z formularza na obiekt i odwrotnie. Nasz sanitizer używał bibioteki JSoup i metody clean do „cichego” wyczyszczenia HTML z danych wejściowych.

Jak już się domyślasz, zaimplementowaliśmy podejście nr 3, czyli „cichego” sprzątacza danych wejściowych. Poniżej dwa główne powody:

  • nie spodziewamy się kodu HTML od zwykłego użytkownika – chyba, że jest to atak XSS;
  • nasza baza danych powinna zawierać czyste dane (adres przekazujemy do web service’ów, więc nie chcemy infekować innych systemów).

Podsumowanie tego posta jest krótkie: Programisto – strzeż się i bądź czujny. Przemyśl dwa razy swoje rozwiązania i zawsze odpal testy bezpieczeństwa Twego formularza!

About the author

piotr.filipowicz

View all posts