5 kroków do bezpieczniejszej aplikacji webowej

przez Mateusz Hoffman
Haker uciekający z płaczem ze strony bloga

Czy Ciebie też ogarnia niemały dreszcz, kiedy czytasz o tym, jak kolejny portal „dzieli się” z cyberprzestępcami swoją bazą danych i obawiasz się, że Twoje ściśle strzeżone hasło „Mojkotpuszek2015” (a to już drugi poziom wtajemniczenia, bo duża litera i liczby) wycieknie z racji, że używasz go od dziesięciu lat w każdym możliwym miejscu?

Nie, pewnie tak nie masz, ale tydzień w tydzień próbujesz przemówić do rozsądku członkom swojej rodziny, żeby przestali tak robić i nie upierali się, że wygrali w tym tygodniu już siódmego iPhone w loterii z pop-upu.

W dzisiejszych czasach zbudowanie prostej aplikacji wymaga poświęcenia kilku godzin na obejrzenie poradnika popularnego youtubera lub użycie gotowego szablonu z GitHub’a i dorzucenie co nam trzeba. Niestety wraz ze wzrostem kompleksowości naszej aplikacji coraz częściej pozostajemy w tym samym modelu myślowym i podczas implementacji wybieramy drogę na skróty, co nie rzadko niesie za sobą opłakane skutki. Jak temu zapobiec?

Mem o bezpieczeństwie IT

W dzisiejszym wpisie przybliżę Wam 5 prostych kroków, które warto podjąć, aby zredukować ryzyko ataku na Waszą aplikację webową. Swoją uwagę skupimy głównie na części klienckiej, aczkolwiek dla zrozumienia całego zjawiska będziemy również nawiązywać do warstwy serwerowej. A oto i one:

  1. Projektowanie
  2. Środowisko
  3. Implementacja
  4. Monitorowanie
  5. Czynnik ludzki

1. Projektowanie

Gdybym miał osiem godzin na ścięcie drzewa, spędziłbym sześć na ostrzeniu siekiery.

Abraham Lincoln

Zabierając się za nowy projekt, często poświęcamy czas na pracę koncepcyjną — rozmyślamy nad funkcjonalnościami, technologią, której użyjemy czy wstępnym projektem graficznym interfejsu. W przeciwieństwie do projektów backendowych, często wpadamy w „pułapkę powierzchowności” skutkującą pominięciem wielu kluczowych elementów, takich jak, chociażby analiza bezpieczeństwa.

Z punktu widzenia budowania designu technicznego niestety zbyt często polegamy na ponownym wykorzystaniu elementów zastosowanych przy poprzednich projektach, technologii wybieranej w oparciu o trend lub okrojony zestaw kompetencji w zespole developerskim.

Zacznij od „dlaczego”

Zadawanie właściwych pytań jest jednym z fundamentów naszego życia — niestety nagminnie unikamy go podczas długofalowych projektów, aby maksymalnie ukrócić jego czas. A gdyby tak „zjeść ciastko i mieć ciastko”? Dla uproszczenia, projektując rozwiązanie warto oprzeć swoją analizę o wymagania i fundamenty biznesowe. Oto kilka pytań, które pomogą Wam określić, które obszary aplikacji wymagały będą szczególnego zainteresowania:

  • Jaki jest cel mojej aplikacji?
  • Kim są jej odbiorcy?
  • Jakie wykonuje operacje?
  • Czy jest to rozwiązanie dostępne publicznie?
  • Jakie dane przetwarzamy?
  • Czy jej użytkownicy różnią się poziomem uprawnień?

Dobór narzędzi oraz zdefiniowanie potencjalnie narażonych elementów naszej aplikacji na etapie projektowania pozwoli nam oszczędzić wiele czasu i pieniędzy na etapie jej utrzymywania i rozwoju.

„Zbyt wysokie progi na Twoje… ataki”

Internet daje nam ogromne możliwości dzielenia się wiedzą i doświadczeniem. Bez Stack Overflow nie istniała by połowa dzisiejszych projektów a w publicznie dostępnych repozytoriach nie znajdowało by się milion identycznych aplikacji to-do. W temacie najlepszych praktyk dt. bezpieczeństwa aplikacji webowych z pomocą wchodzi on, cały na biało — OWASP Foundation.

Według Wikipedii:

OWASP (Open Web Application Security Project) to społeczność internetowa, która produkuje ogólnodostępne artykuły, metodologie, dokumentację, narzędzia i technologie z zakresu bezpieczeństwa aplikacji internetowych.

Jednym z ciekawszych rzeczy jakie dostarczyła nam ta organizacja jest koncept „Security by Design”, który definiuje 10 fundamentalnych zasad podczas budowania aplikacji webowych takich jak:

  • Minimalizacja powierzchni ataku
  • Ustanawianie bezpieczeństwa domyślnie
  • Zasada najniższego poziomu dostępu (POLP)

Po pełną listę i jej opis odsyłam do oryginalnego artykułu ThreatPress.

2. Środowisko

No dobra, wszystko fajnie mamy dogadane, to teraz wskakujemy przed biurko i zaczynamy pisać kod jak szaleni… chyba że jednak nie. Elementem układanki, o którym stosunkowo często zapominamy z racji przyzwyczajeń i bezwarunkowego zaufania jest kwestia środowiska na, którym przetrzymujemy swoje dane.

Dostawca to zły czy dobry glina?

Planując budżet na realizacje projektu, staramy się wyszukiwać różnego rodzaju oszczędności, które mają pozornie minimalny wpływ na codzienne funkcjonowanie aplikacji. O ile w dużych organizacjach dodatkowym faktorem przy wyborze staje się obecność dostawcy usług/serwera na „korporacyjnej whiteliście” a skala projektów często wyklucza mniejszych graczy, o tyle w przypadku mniejszych firm lub też zleceń realizowanych przez pojedynczych freelancerów, sprawa dostawcy usług sprowadza się do otwarcia internetowego rankingu dostawców, posortowania po cenie i cyk, decyzja podjęta.

Pamiętajmy jednak o tym, że nasz wybór niesie za sobą dwa ryzyka (niezależnie od poziomu zabezpieczeń i super-kodu naszej aplikacji):

  • Narażenie ze strony firmy o szemranej reputacji – przetrzymywanie danych oznacza również ich powierzenie w czyjeś ręce. Warto być pewnym, że są to ręce uczciwe i czyste. Tutaj minimalizacja zagrożenia opiera się na analizie warunków świadczenia usługi, zdefiniowanego SLA (Service Level Agreement) oraz reputacji na rynku.
  • Poziom zabezpieczeń usługodawcy – pamiętajmy o tym, że nasz dostawca mimo szczerych chęci czy intencji, może być dodatkową płaszczyzną ataku cyberprzestępców aktywnie szukających podatności w otoczeniu. Pomocne mogą okazać się podobne czynności jak w punkcie powyżej (warunki przechowywania danych, polityka kopii zapasowych itp)
Wycinek artykułu o wycieku bazy firmy Hostinger

Źródło: Techno Senior (https://techno-senior.com/2019/08/28/zhakowano-baze-14-milionow-kont-klientow-popularnego-hostingu/)

Certyfikaty SSL a bezpieczeństwo

Jednym z bardzo powszechnych mitów, które warto obalić jest relacja pomiędzy certyfikatem SSL a bezpieczeństwem aplikacji. Obecność słynnego „https’a” oznacza, że jedynie połączenie pomiędzy użytkownikiem jest szyfrowane, co zabezpiecza nas, chociażby przed takimi atakami jak MITM (man-in-the-middle). Jego stosowanie jest zdecydowanie krokiem w dobrym kierunku aczkolwiek nie gwarantuje to nam jako użytkownikowi żadnego bezpieczeństwa przed samym potencjalnym wrogiem w postaci strony z której korzystamy.

Dodatkowe punkty

  • STOP dla bezmyślnego commit’owania plików .env (biblioteka dotenv służy do definiowania lokalnych zmiennych środowiskowych) do publicznych repozytoriów takich jak GitHub. Ratunkiem może być tutaj dodanie pliku .env do listy ignorowanych plików przez git’a (gitignore)
  • Wykrywanie niebezpiecznych fragmentów kodu na etapie wytwórczym przez dodatki do Linterów (statyczna analiza kodu). Jednym z przykładów dla języka Javascript może być tutaj eslist-plugin-security.

3. Implementacja

Teraz kiedy już spokojnie możemy przejść do tego, „co tygryski lubią najbardziej” czyli pisania kodu, warto oddzielić grubą kreską dwa rodzaje zagrożeń a raczej ich genezy:

  • Zagrożenia, które stwarzamy sobie sami
  • Zagrożenia, które stawiają przed nami inni

Zdaje sobie sprawę, że temat jest bardzo obszerny dlatego też postaram się opowiedzieć Ci jedynie o kilku wybranych na potrzebę tego wpisu. Klawiatura w dłoń (a raczej na biurko) i lecimy!

Zagrożenia, które stwarzamy sobie sami

Nie uważam, że każdy z nas ma tendencje do podkładania sobie kłód pod nogi — mam za to wrażenie, że części z nas sprawdziła się wróżba nauczycielek z podstawówki mówiących: „No zdolny jest ale leniwy!”

  • Brak walidacji wykonywanych operacji – biorąc pod uwagę łatwość z jaką możemy manipulować informacjami dostępnymi po stronie klienckiej, podkręcenie mechanizmu routingu często ogranicza się do podmiany wartości kilku zmiennych w odpowiednim momencie by z mniejszym lub większym problemem nawigować po widokach dla nas nie przeznaczonych. Ot taka natura front-endu. Warto jednak pamiętać, że poprawnie zaprojektowany system opuszcza kolejne ściany zabezpieczeń przed twarzą upartego cyberprzestępcy — otwarcie panelu administratora de facto nie powinno umożliwiać mu wykonywania żadnych operacji czy odczytu jakichkolwiek danych. Bohaterem stojącym za kurtyną jest tutaj część serwerowa, która odpowiednio udziela bądź zabiera nawigującemu dostępu do odpytywanych endpointów. Proste? Jeszcze jak!
  • Przetrzymywanie martwego kodu – w potocznym języku tak zwane „przydasie” (dzięki Przemek za podpowiedź). Nieużywane pokłady Javascriptu/CSS leżą w naszych plikach źródłowych i czekają na lepsze czasy niczym słoik po zjedzonym majonezie myty i chowany do szafki z myślą, że kiedyś go jeszcze wykorzystasz. O ile kolejne pudełko czy słoik nie zrobią nam większej krzywdy tak pozostawione samemu sobie martwe funkcjonalności oddają atakującemu do dyspozycji dodatkową, podatną płaszczyznę do realizacji swoich niecnych planów. Ratunkiem jest tutaj zwyczajne pilnowanie czystości kodu czy funkcjonalność tree-shaking’u dostępna chociażby w bundlerach pokroju Webpack’a.

Zagrożenia, które stawiają przed nami inni

To już bardziej powszechna kategoria, z którą spotkamy się podczas przeczesywania poradników dotyczących cyberbezpieczeństwa. Dotyczy ona wykorzystywania naturalnych możliwości/podatności używanej przez nas technologii lub też wykorzystuje te elementy, które autor zwyczajnie przegapił podczas wdrażania w życie swojego projektu.

Cross-site scripting (XSS)

Technika ataku polegająca na wstrzyknięciu złośliwego skryptu do przeglądarki internetowej ofiary poprzez wykorzystanie elementów służących do wprowadzenia danych wejściowych. Skrypt wykonując się w sposób niezauważalny dla użytkownika, wykorzystuje fakt działania z poziomu zaufanego źródła, co może poskutkować kradzieżą danych wrażliwych takich jak tokeny, informacje dotyczące sesji oraz wszelkich przechowywanych ciasteczek.

Schemat działania XSS

Źródło: PortSwigger (portswigger.net)

Rozróżniamy jego trzy typy:

    • Przechowywany (stored) – w dużym skrócie, złośliwy kod wprowadzony przez atakującego, zostaje wysłany do serwera, który przechowuje go w jakiejś formie (np. baza danych). Następnie każdy użytkownik wyświetlający tę zawartość (lista/post/cokolwiek) zostaje „udekorowany” wykonaniem takowego skryptu, co może nieść za sobą chociażby kradzież jego danych sesyjnych i umożliwić atakującemu zalogowanie się w imieniu ofiary.
    • Bazujący na DOM’ie – jego nazwa w dużym stopniu wyjaśnia istotę jego działania. Atakujący wykorzystuje tutaj Document Object Model (reprezentacje struktury dokumentu), co oznacza że manipulacja odbywa się jedynie po stronie klienta a sama odpowiedź serwerowa nie ulega zmianie.
    • Reflected (jako, że odbity/odzwierciedlony brzmi źle) – ten typ wykorzystuje siłę parametrów przekazywanych do serwera. Efekt lustrzanego odbicia polega na bezwiednym przetworzeniu wartości przekazanej np. za pomocą uprzednio przygotowanego linku zawierającego HTML / kod Javascript przez część serwerową a następnie zaserwowanie nam jej ponownie wraz wykonującym się złośliwym skryptem.

Najprostszą metodą ochrony przed tego typu atakami jest odpowiednie czyszczenie (sanitacja) oraz walidacja elementów służących do przekazywania danych wejściowych (np. inputy). Dwa przykłady takich bibliotek to yup (schema validation) oraz DOMPurify (sanitacja inputów).

OWASP Top Ten

Omówiliśmy jeden z najpopularniejszych ataków skupiający się w dużym stopniu na części klienckiej aczkolwiek jest tego znacznie więcej. Wymieniana już kilkukrotnie organizacja zwana OWASP, stworzyła listę dziesięciu najbardziej powszechnych zagrożeń dotykających aplikacji webowych. Znajdziemy tam:

  1. Wstrzykiwanie (iniekcja)
  2. Błędnie zaimplementowany mechanizm uwierzytelniania
  3. Wystawianie wrażliwych danych
  4. XML External Entities (XXE)
  5. Błędnie zaimplementowane zarządzanie dostępem
  6. Braki w konfiguracji bezpieczeństwa
  7. Cross-Site Scripting (XSS)
  8. Niezabieczona deserializacja
  9. Używanie zewnętrznych komponentów z podatnościami
  10. Niewystarczające logowanie oraz monitorowanie

Po pełną listę wraz z opisem i proponowanymi metodami prewencji zapraszam do oryginalnego artykułu.

4. Monitorowanie

Po upewnieniu się, że nasza aplikacja została wyposażona we wszelkiego rodzaju zabezpieczenia od strony środowiska jak i samego kodu, bardzo ważnym krokiem, który powinniśmy uskuteczniać jest monitorowanie naszego rozwiązania. W obecnych czasach, praca setek organizacji jak i wolontariuszy pozwoliła na stworzenie wielu narzędzi służących do wykonywania audytów i analiz naszych projektów — często są one udostępniane za darmo a ich obsługa nie wymaga od nas specjalistycznej wiedzy. Jako, że nie jest to poradnik ściśle dotyczący testowania, pozostawię Was z moją listą kilku ciekawych propozycji do rozważenia:

  • Google Lighthouse Audit / WEB.DEV Measure – podstawowe narzędzie dostępne zarówno za pośrednictwem DevToolsów przeglądarki Google Chrome jak i poprzez aplikację webową. Pomimo, że kojarzona jest ona głównie z badaniem wydajności naszej strony, daje ona nam również wczesne sygnały dotyczące niedochowanych praktyk kodowych mogących wpłynąć na bezpieczeństwo naszej aplikacji. Dwa w jednym poprzez kilka kliknięć? Ja to kupuje.
  • NPM Audit – narzędzie pomagające nam zaadresować dziewiąty element listy OWASP TOP 10 czyli podatne biblioteki 3rd party. Warto pamiętać, że odpowiednie zabezpieczenie naszego kodu nie chroni nas całkowicie przez wpływem ataków przeprowadzanych na płaszczyźnie bibliotek zewnętrznych. Audit to wbudowane w NPM narzędzie analizujące używane przez nas paczki i dające nam w efekcie raport podatności wykrytych w ich konkretnych wersjach. Dbajmy o zaopatrywanie się w najnowsze poprawki.
  • OWASP Zap – prosty i darmowy skaner aplikacji webowych od naszej ulubionej organizacji OWASP Foundation. Działa na zasadzie proxy, umiejscowionego pomiędzy częścią kliencką a serwerem — stara się tam odpowiednio wpłynąć i zmanipulować wymieniane przez aplikacje requesty a następnie komponuje nam piękny raport dt. wykrytych zagrożeń i informacji o ich możliwym załataniu
  • Security Headers (powered by Probely) – przydatna aplikacja analizująca nagłówki odpowiedzi HTTP. Efektem jej działania jest lista obecnych/brakujących nagłówków wpływająca na bezpieczeństwo naszej aplikacji – wszystko to okraszone ogólną oceną, która może sugerować ile pracy już za nami a ile jeszcze na nas czeka. Dbajmy o nagłówki, warto.
  • Burp Suite – bardziej zaawansowane narzędzie służące do przeprowadzania testów penetracyjnych. W darmowej wersji nazwanej „Community” uzyskujemy dostęp do wielu narzędzi, które manualnie możemy wykorzystać do testowania naszej witryny. Za wszelkiego rodzaju automatyczne skany należy niestety zapłacić.

5. Czynnik ludzki

Można by pomyśleć, że lista kończy się na etapie poskładania wszystkich elementów związanych z przygotowaniem i implementacją naszego rozwiązania. Często zapominamy jednak o tym, że dosyć podatnym elementem w całej układance jesteśmy my sami. Hakerzy co raz to częściej wykorzystują słabości ludzkiej psychiki do utorowania sobie drogi do naszej ściśle strzeżonej cyber-fortecy.

Jednym z bardziej znanych przykładów wykorzystania socjotechniki do łamania systemów informatycznych jest działalność Kevina Mitnicka, nazywanego jednym z największych hakerów wszech czasów. Wykorzystywał on brak czujności pracowników wielkich organizacji, by bezszelestnie uzyskiwać dostęp do najściślej strzeżonych danych oraz systemów. Jego historia została uwieczniona w książce „Sztuka podstępu. Łamałem ludzi, nie hasła” którą gorąco polecam.

Tak więc dbajmy również o edukację z zakresu cyberbezpieczeństwa w gronie swoich znajomych, współpracowników oraz rodziny.

Podsumowując

Pisząc ten artykuł uświadomiłem sobie jak ciężko jest przekazać tak istotną i ważną wiedzę w sposób kompaktowy i zrozumiały. Uproszczenie w postaci prezentowanych 5 kroków pozwala na zamknięcie fundamentów bezpieczeństwa aplikacji webowych w małe wiaderka, które należy rozważyć podczas budowania rozwiązań w przyszłości. Są nimi:

  1. Projektowanie – dbałość o identyfikacje zagrożeń już na etapie koncepcyjnym
  2. Środowisko – sprawdzenie warunków w jakich przechowujemy nasze usługi/kod/projekt
  3. Implementacja – budowanie funkcjonalności mając na uwadze najlepsze praktyki dt. bezpieczeństwa
  4. Monitorowanie – ciągła analiza i obserwacja naszego projektu, pozwalająca na odpowiednio szybką reakcję w razie zagrożenia
  5. Czynnik ludzki – dbanie o edukację z zakresu cyberbezpieczeństwa i świadomość, że człowiek jest kluczowym elementem całego łańcucha

Mam nadzieje, że wpis Ci się spodobał i wniesie wartość do Twojej pracy przy tworzeniu aplikacji internetowych.

Trzymaj się ciepło,

Darmowy ebook dla subkrybentów