Izolacja witryn dla programistów stron internetowych

W Chrome 67 na komputerach dostępna jest nowa funkcja o nazwie Izolacja witryn włączona domyślnie. Z tego artykułu dowiesz się, na czym polega izolacja witryn, dlaczego jest konieczna i dlaczego deweloperzy stron internetowych powinni o niej wiedzieć.

Co to jest izolacja witryn?

Internet służy m.in. do oglądania filmów z kotami i zarządzania portfelami kryptowalutowymi, ale nie chcesz, żeby fluffycats.example miał dostęp do Twoich cennych kryptowalut. Na szczęście, dzięki zasadzie Same-Origin, witryny zwykle nie mają dostępu do swoich danych w przeglądarce. Złośliwe witryny mogą jednak próbować obejść tę zasadę, aby atakować inne strony. Czasem zdarza się, że w kodzie przeglądarki znajdują się błędy w zabezpieczeniach, które wymuszają stosowanie zasady Same-Origin. Zespół Chrome stara się jak najszybciej usuwać takie błędy.

Izolacja witryn to funkcja zabezpieczeń w Chrome, która zapewnia dodatkową linię obrony, dzięki czemu takie ataki są mniej prawdopodobne. Dzięki temu strony z różnych witryn zawsze są poddawane innym procesom, z których każda działa w piaskownicy, która ogranicza to, co ten proces może robić. Blokuje też procesowi otrzymywanie określonych typów danych wrażliwych z innych witryn. W efekcie przy izolacji witryn znacznie trudniej jest wykraść dane z innych witryn za pomocą spekulacyjnych ataków po stronie klienta, takich jak Spectre. Gdy zespół Chrome zakończy wprowadzanie dodatkowych procedur egzekwowania zasad, zastosowanie izolacji witryn pomoże również w sytuacji, gdy strona przeprowadzająca atak może złamać niektóre reguły w ramach jej własnego procesu.

Izolacja witryn w efekcie utrudnia niezaufanym witrynom dostęp do informacji na Twoich kontach w innych witrynach oraz wykradanie z nich informacji. Zapewnia dodatkową ochronę przed różnego rodzaju błędami w zabezpieczeniach, takimi jak niedawne ataki typu Meltdown czy Spectre.

Więcej informacji na temat izolacji witryn znajdziesz w naszym artykule na blogu Google o bezpieczeństwie w Google.

Blokowanie odczytu między serwerami z różnych źródeł

Nawet wtedy, gdy wszystkie strony w różnych witrynach są objęte oddzielnymi procesami, strony mogą nadal w sposób uzasadniony żądania dostępu do określonych zasobów podrzędnych, np. obrazów i JavaScriptu. Złośliwa strona internetowa może użyć elementu <img> do wczytania pliku JSON z danymi wrażliwymi, takimi jak saldo w banku:

<img src="https://your-bank.example/balance.json" />
<!-- Note: the attacker refused to add an `alt` attribute, for extra evil points. -->

Bez izolacji witryn zawartość pliku JSON dotarłaby do pamięci procesu renderowania, wówczas mechanizm renderowania zauważyłby, że nie jest to prawidłowy format obrazu, więc nie renderowałby obrazu. Osoba przeprowadzająca atak może jednak wykorzystać lukę w zabezpieczeniach taką jak Spectre, aby odczytać ten fragment pamięci.

Zamiast używać <img>, atakujący może też użyć metody <script>, aby zapisać dane wrażliwe w pamięci:

<script src="https://your-bank.example/balance.json"></script>

Cross-Origin Read Block (CORB) to nowa funkcja zabezpieczeń, która na podstawie typu MIME zapobiega wniknięciu zawartości pamięci balance.json przez mechanizm renderowania do pamięci.

Przyjrzyjmy się działaniu CORB. Witryna może żądać od serwera 2 typów zasobów:

  1. zasoby danych, takie jak dokumenty HTML, XML lub JSON;
  2. zasoby multimedialne, takie jak obrazy, JavaScript, CSS czy czcionki;

Witryna może otrzymywać zasoby danych z własnego źródła lub z innych źródeł za pomocą mniej rygorystycznych nagłówków CORS, np. Access-Control-Allow-Origin: *. Z drugiej strony zasoby multimedialne mogą być uwzględnione z dowolnego źródła, nawet bez odpowiednich nagłówków CORS.

CORB zapobiega odbieraniu przez proces renderowania zasobu danych z innych domen (np. HTML, XML lub JSON), jeśli:

  • zasób ma nagłówek X-Content-Type-Options: nosniff
  • CORS nie zezwala bezpośrednio na dostęp do zasobu

Jeśli zasób danych z innych domen nie ma ustawionego nagłówka X-Content-Type-Options: nosniff, CORB próbuje przechwycić treść odpowiedzi, aby ustalić, czy jest ona w formacie HTML, XML czy JSON. Jest to konieczne, ponieważ niektóre serwery WWW są nieprawidłowo skonfigurowane i wyświetlają obrazy, na przykład jako text/html.

Zasoby danych zablokowane przez zasadę CORB są przedstawiane procesowi jako puste, chociaż żądanie nadal odbywa się w tle. W rezultacie złośliwa strona internetowa ma trudności z pobieraniem danych z innych witryn do swojego procesu, aby je wykraść.

Aby uzyskać optymalne bezpieczeństwo i wykorzystać CORB, zalecamy te działania:

  • Oznaczaj odpowiedzi prawidłowym nagłówkiem Content-Type. Na przykład zasoby HTML powinny być udostępniane jako text/html, zasoby JSON z typem MIME JSON i zasoby XML z typem MIME XML.
  • Zrezygnuj z przechwytywania, używając nagłówka X-Content-Type-Options: nosniff. Bez tego nagłówka Chrome przeprowadza szybką analizę treści, aby sprawdzić, czy typ jest prawidłowy. Jednak w przeciwnym razie brak możliwości przepuszczania odpowiedzi w celu uniknięcia blokowania elementów (np. plików JavaScript), więc lepiej robić to samodzielnie.

Więcej informacji znajdziesz w artykule na temat CORB dla programistów stron internetowych lub naszym szczegółowym wyjaśnieniu dotyczącym CORB.

Dlaczego deweloperzy stron internetowych powinni zadbać o izolację witryn?

Izolacja witryn to przeważnie funkcja przeglądarki, która nie jest bezpośrednio dostępna dla programistów. Nie ma na przykład nowego interfejsu API wykorzystywanego w internecie do nauki. Zazwyczaj strony internetowe nie powinny być w stanie odróżnić, jeśli działają z izolacją witryny lub bez niej.

Od tej reguły są jednak pewne wyjątki. Włączenie izolacji witryn wiąże się z kilkoma nieistotnymi efektami ubocznymi, które mogą mieć wpływ na witrynę. Poniżej znajduje się lista znanych problemów z izolacją witryn, a najważniejsze z nich omawiamy poniżej.

Układ całej strony nie jest już synchroniczny

W przypadku izolacji witryn układ całej strony nie musi być już synchroniczny, ponieważ ramki strony mogą się teraz rozdzielić na wiele procesów. Może to mieć wpływ na strony, jeśli przyjmą, że zmiana układu została natychmiast zastosowana do wszystkich ramek na stronie.

Weźmy za przykład witrynę o nazwie fluffykittens.example, która komunikuje się z widżetem społecznościowym hostowanym w social-widget.example:

<!-- https://fluffykittens.example/ -->
<iframe src="https://social-widget.example/" width="123"></iframe>
<script>
  const iframe = document.querySelector('iframe');
  iframe.width = 456;
  iframe.contentWindow.postMessage(
    // The message to send:
    'Meow!',
    // The target origin:
    'https://social-widget.example'
  );
</script>

Początkowo widżet społecznościowy (<iframe>) ma szerokość 123 pikseli. Jednak później strona FluffyKittens zmieni szerokość na 456 piks. (układ reguły) i wyśle wiadomość do widżetu społecznościowego, który zawiera ten kod:

<!-- https://social-widget.example/ -->
<script>
  self.onmessage = () => {
    console.log(document.documentElement.clientWidth);
  };
</script>

Gdy widżet społecznościowy odbiera wiadomość przez interfejs API postMessage, rejestruje szerokość swojego głównego elementu <html>.

Która wartość szerokości jest rejestrowana? Przed włączeniem izolacji witryn w Chrome odpowiedź brzmiała 456. Dostęp do document.documentElement.clientWidth wymusza układ, który przed włączeniem izolacji witryn w Chrome był synchroniczny. Jednak przy włączonej izolacji witryn udostępnianie widżetu społecznościowego z innych domen odbywa się teraz asynchronicznie w osobnym procesie. Dlatego odpowiedzią może być teraz także 123, czyli stara wartość width.

Jeśli strona zmieni rozmiar pliku <iframe> z innych domen, a potem wyśle do niej tag postMessage, w przypadku izolacji witryn ramka odbierająca może jeszcze nie znać swojego nowego rozmiaru w chwili odebrania wiadomości. Ogólnie rzecz biorąc, może to powodować błędy na stronach, jeśli przy założeniu, że zmiana układu zostanie natychmiast zastosowana do wszystkich ramek na stronie.

W tym przykładzie bardziej niezawodne rozwiązanie ustawi width w ramce nadrzędnej i wykryje tę zmianę w elemencie <iframe> przez nasłuchiwanie zdarzenia resize.

Niezaładowane moduły obsługi mogą częściej przekraczać limit czasu

Gdy ramka przechodzi lub się zamyka, stary dokument oraz umieszczone w niej dokumenty ramki podrzędnej uruchamiają swoje moduły obsługi unload. Jeśli nowa nawigacja odbywa się w tym samym procesie renderowania (np. w przypadku nawigacji po tej samej domenie), moduły obsługi unload starego dokumentu i jego ramek podrzędnych mogą działać dowolnie długo, zanim zezwolisz na zatwierdzenie nowej nawigacji.

addEventListener('unload', () => {
  doSomethingThatMightTakeALongTime();
});

W takiej sytuacji moduły obsługi unload we wszystkich klatkach są bardzo niezawodne.

Jednak nawet bez izolacji witryn niektóre elementy nawigacyjne w ramkach głównej są wykonywane w różnych procesach, co wpływa na sposób działania modułu obsługi wyładowywania. Jeśli np. przejdziesz z old.example do new.example, wpisując adres URL w pasku adresu, nawigacja w usłudze new.example zostanie przeprowadzona w nowym procesie. Moduły obsługi wyładowywania elementu old.example i jego ramek podrzędnych uruchamiają się w procesie old.example w tle, po pokazaniu strony new.example. Stare moduły wyładowania nie działają, jeśli nie zostaną zakończone, jeśli nie zakończy się to po upływie określonego czasu oczekiwania. Moduły wyładowania mogą nie zakończyć się przed upływem limitu czasu, więc sposób wyładowywania jest mniej miarodajny.

W przypadku izolacji witryn wszystkie operacje nawigacji między witrynami są przeprowadzane na różnych stronach, dzięki czemu dokumenty z różnych witryn nie współdzielą procesu. W rezultacie ta sytuacja ma miejsce w większej liczbie przypadków, a moduły wyładowywania z komórek <iframe> często mają opisane powyżej działanie w tle i z czasem oczekiwania.

Kolejną różnicą wynikającą z izolacji witryn jest nowa, równoległe ustalanie kolejności modułów unload: bez izolacji witryn moduły obsługi unload działają w ścisłej kolejności od góry do wielu ramek. Jednak przy izolacji witryn moduły obsługi wyładowania działają równolegle w różnych procesach.

To fundamentalne konsekwencje włączenia izolacji witryn. Zespół Chrome pracuje nad zwiększeniem niezawodności modułów wyładowywania stron w typowych przypadkach użycia, jeśli jest to możliwe. Zdajemy sobie również sprawę z błędów powodujących, że moduły obsługi wyładowania z ramki podrzędnej nie mogą jeszcze korzystać z określonych funkcji i pracują nad ich rozwiązaniem.

Ważnym przypadkiem wyładowywania elementów jest wysyłanie pingów na koniec sesji. Zwykle robi się to w ten sposób:

addEventListener('pagehide', () => {
  const image = new Image();
  img.src = '/end-of-session';
});

W świetle tej zmiany lepszym podejściem jest użycie parametru navigator.sendBeacon:

addEventListener('pagehide', () => {
  navigator.sendBeacon('/end-of-session');
});

Jeśli chcesz mieć większą kontrolę nad żądaniem, możesz użyć opcji keepalive interfejsu Fetch API:

addEventListener('pagehide', () => {
  fetch('/end-of-session', {keepalive: true});
});

Podsumowanie

Izolacja witryn utrudnia niezaufanym witrynom uzyskiwanie dostępu do informacji na Twoich kontach w innych witrynach lub wykradanie z nich informacji, ponieważ każda witryna ma własny proces. W związku z tym CORB stara się, aby zasoby danych wrażliwych nie były uwzględniane w procesie renderowania. Nasze zalecenia pomogą Ci w pełni wykorzystać możliwości nowych funkcji zabezpieczeń.

Pozdrawiamy