Bezpieczne przechowywanie danych użytkowników w nowoczesnych aplikacjach internetowych

David Dworken
David Dworken

Wiele aplikacji internetowych musi wyświetlać treści kontrolowane przez użytkowników. Może to być tak proste, jak wyświetlanie obrazów przesłanych przez użytkowników (np. zdjęć profilowych), lub tak złożone, jak renderowanie kodu HTML kontrolowanego przez użytkownika (np. w samouczku dotyczącym tworzenia stron internetowych). Zawsze było to trudne do zrobienia w bezpieczny sposób, dlatego staraliśmy się znaleźć proste, ale bezpieczne rozwiązania, które można zastosować w większości typów aplikacji internetowych.

Klasyczne rozwiązania do izolowania niezaufanych treści

Klasycznym rozwiązaniem do bezpiecznego wyświetlania treści kontrolowanych przez użytkownika jest używanie tzw. domen piaskownicy. Podstawowa zasada jest taka, że jeśli główna domena aplikacji to example.com, możesz wyświetlać wszystkie niezaufane treści w domenie exampleusercontent.com. Ponieważ te 2 domeny są wieloma witrynami, żadne złośliwe treści w domenie exampleusercontent.com nie mogą mieć wpływu na domenę example.com. Takie podejście pozwala bezpiecznie udostępniać wszystkie rodzaje niezaufanych treści, w tym obrazy, pliki do pobrania i HTML. Chociaż może się wydawać, że nie jest to konieczne w przypadku obrazów lub pobieranych plików, pomaga to uniknąć ryzyka związanego z wykrywaniem typu treści, zwłaszcza w starszych przeglądarkach. Domena piaskownicy jest powszechnie stosowana w branży i od dawna dobrze się sprawdza. Mają jednak 2 główne wady:

  • Aplikacje często muszą ograniczać dostęp do treści do jednego użytkownika, co wymaga wdrożenia uwierzytelniania i autoryzacji. Ponieważ domeny piaskownicy celowo nie udostępniają plików cookie domenie głównej aplikacji, bezpieczne wykonanie tej czynności jest bardzo trudne. Aby obsługiwać uwierzytelnianie, witryny muszą korzystać z adresów URL funkcji lub ustawiać osobne pliki cookie uwierzytelniania dla domeny piaskownicy. Ta druga metoda jest szczególnie problematyczna w nowoczesnej sieci, w której wiele przeglądarek domyślnie ogranicza pliki cookie innych witryn.
  • Treści użytkowników są odseparowane od głównej witryny, ale nie od innych treści użytkowników. Stwarza to ryzyko, że złośliwe treści użytkownika zaatakują inne dane w domenie piaskownicy (np. odczytując dane tego samego pochodzenia).

Warto też zauważyć, że domeny w piaskownicy pomagają ograniczać ryzyko wyłudzania informacji, ponieważ zasoby są wyraźnie podzielone na odizolowane domeny.

Nowoczesne rozwiązania do wyświetlania treści użytkowników

Internet z czasem się rozwinął i obecnie istnieją łatwiejsze i bezpieczniejsze sposoby wyświetlania niezaufanych treści. Istnieje wiele różnych podejść, dlatego przedstawimy 2 rozwiązania, których używamy w Google.

Podejście 1. Udostępnianie treści użytkownika nieaktywnym użytkownikom

Jeśli witryna musi udostępniać tylko nieaktywne treści użytkownika (czyli zawartość, która nie jest kodem HTML ani JavaScript, np. obrazy i pliki do pobrania), można to teraz bezpiecznie zrobić bez izolowanej domeny piaskownicy. W tym celu wykonaj 2 główne kroki:

  • Zawsze ustawiaj nagłówek Content-Type na dobrze znany typ MIME, który jest obsługiwany przez wszystkie przeglądarki i nie zawiera aktywnej zawartości. W razie wątpliwości bezpiecznym wyborem jest application/octet-stream.
  • Dodatkowo zawsze ustawiaj nagłówki odpowiedzi, aby mieć pewność, że przeglądarka w pełni izoluje odpowiedź.
Nagłówek odpowiedzi Purpose

X-Content-Type-Options: nosniff

Zapobiega wykrywaniu treści

Content-Disposition: attachment; filename="download"

Wywołuje pobieranie zamiast renderowania

Content-Security-Policy: sandbox

Umieszcza treści w piaskownicy tak, jakby były wyświetlane w oddzielnej domenie.

Content-Security-Policy: default-src ‘none'

Wyłącza wykonywanie JavaScriptu (i uwzględnianie wszelkich zasobów podrzędnych).

Cross-Origin-Resource-Policy: same-site

uniemożliwia włączenie strony w innej witrynie;

Ta kombinacja nagłówków zapewnia, że odpowiedź może być wczytywana jako zasób podrzędny tylko przez Twoją aplikację lub pobierana jako plik przez użytkownika. Ponadto nagłówki zapewniają wiele warstw ochrony przed błędami przeglądarki dzięki nagłówkowi piaskownicy CSP i ograniczeniu default-src. Ogólnie rzecz biorąc, opisana konfiguracja zapewnia wysoki stopień pewności, że odpowiedzi wyświetlane w ten sposób nie mogą prowadzić do luk wstrzykiwania ani luk w zabezpieczeniach izolacji.

Obrona w głąb

Proponowane rozwiązanie stanowi ogólnie wystarczającą ochronę przed atakami XSS, ale możesz zastosować szereg dodatkowych środków wzmacniających, aby zapewnić dodatkowe warstwy zabezpieczeń:

  • Ustaw X-Content-Security-Policy: sandboxnagłówek, aby zapewnić zgodność z IE11.
  • Ustaw nagłówek Content-Security-Policy: frame-ancestors 'none', aby zablokować osadzanie punktu końcowego.
  • Treści użytkowników w piaskownicy w izolowanej subdomenie:
    • Wyświetlanie treści użytkowników w izolowanej subdomenie (np. Google używa domen takich jak product.usercontent.google.com).
    • Ustaw wartości Cross-Origin-Opener-Policy: same-originCross-Origin-Embedder-Policy: require-corp, aby włączyć izolację zasobów z różnych domen.

Podejście 2. Wyświetlanie treści użytkownika aktywnym użytkownikom

Bezpieczne wyświetlanie aktywnych treści (np. obrazów HTML lub SVG) jest też możliwe bez wad klasycznego podejścia do domeny piaskownicy.

Najprostszym rozwiązaniem jest użycie nagłówka Content-Security-Policy: sandbox, aby poinformować przeglądarkę o konieczności odizolowania odpowiedzi. Nie wszystkie przeglądarki internetowe implementują izolację procesów w przypadku dokumentów w piaskownicy, ale ciągłe udoskonalanie modeli procesów przeglądarki prawdopodobnie poprawi separację treści w piaskownicy od aplikacji osadzających. Jeśli ataki SpectreJSrenderer compromise nie mieszczą się w Twoim modelu zagrożeń, użycie piaskownicy CSP prawdopodobnie będzie wystarczającym rozwiązaniem. W Google opracowaliśmy rozwiązanie, które umożliwia pełne odizolowanie niezaufanych aktywnych treści dzięki unowocześnieniu koncepcji domen piaskownicy. Główna idea polega na:

  • Utwórz nową domenę piaskownicy, która zostanie dodana do listy sufiksów publicznych. Na przykład dodając exampleusercontent.com do listy PSL, możesz mieć pewność, że foo.exampleusercontent.combar.exampleusercontent.com są domenami różnych witryn, a tym samym są od siebie całkowicie odizolowane.
  • Adresy URL pasujące do wzorca *.exampleusercontent.com/shim są kierowane do statycznego pliku shim. Ten plik shim zawiera krótki fragment kodu HTML i JavaScript, który nasłuchuje procedury obsługi zdarzeń message i renderuje wszystkie otrzymane treści.
  • Aby to zrobić, usługa tworzy element iframe lub okno dialogowe, aby $RANDOM_VALUE.exampleusercontent.com/shim, i używa postMessage do wysyłania niezaufanych treści do warstwy pośredniej w celu renderowania.
  • Wyrenderowana treść jest przekształcana w obiekt Blob i wyświetlana w elemencie iframe umieszczonym w piaskownicy.

W porównaniu z klasycznym podejściem do domeny piaskownicy zapewnia to pełną izolację wszystkich treści w unikalnej witrynie. Gdy główna aplikacja zajmuje się pobieraniem danych do renderowania, nie trzeba już używać adresów URL funkcji.

Podsumowanie

Te 2 rozwiązania umożliwiają przejście z klasycznych domen piaskownicy, takich jak googleusercontent.com, na bezpieczniejsze rozwiązania, które są zgodne z blokowaniem plików cookie innych firm. W Google wiele usług zostało już przeniesionych na te rozwiązania, a w przyszłym roku planujemy kolejne migracje.