Изоляция сайта для веб-разработчиков

В Chrome 67 для настольных компьютеров новая функция под названием «Изоляция сайта» включена по умолчанию . В этой статье объясняется, что такое изоляция сайта, почему она необходима и почему веб-разработчикам следует об этом знать.

Что такое изоляция сайта?

Интернет, среди прочего, предназначен для просмотра видео с котиками и управления криптовалютными кошельками, но вы бы не хотели, чтобы fluffycats.example имел доступ к вашим драгоценным криптовалютам! К счастью, веб-сайты обычно не могут получить доступ к данным друг друга внутри браузера благодаря политике одинакового происхождения. Тем не менее, вредоносные веб-сайты могут попытаться обойти эту политику для атаки на другие веб-сайты, и иногда в коде браузера обнаруживаются ошибки безопасности, которые обеспечивают соблюдение политики одного и того же происхождения. Команда Chrome стремится исправить такие ошибки как можно быстрее.

Изоляция сайта — это функция безопасности в Chrome, которая предлагает дополнительную линию защиты, снижающую вероятность успеха таких атак. Это гарантирует, что страницы с разных веб-сайтов всегда помещаются в разные процессы, каждый из которых выполняется в «песочнице», которая ограничивает возможности процесса. Он также блокирует процесс получения определенных типов конфиденциальных данных с других сайтов. В результате при использовании изоляции сайта вредоносному веб-сайту гораздо сложнее использовать спекулятивные атаки по побочным каналам, такие как Spectre, для кражи данных с других сайтов. Поскольку команда Chrome завершает дополнительные меры по обеспечению соблюдения требований, изоляция сайта также поможет, даже если страница злоумышленника может нарушить некоторые правила в своем собственном процессе.

Изоляция сайта эффективно затрудняет доступ ненадежных веб-сайтов к вашим учетным записям на других веб-сайтах или их кражу. Он предлагает дополнительную защиту от различных типов ошибок безопасности, таких как недавние атаки по побочным каналам Meltdown и Spectre .

Более подробную информацию об изоляции сайтов можно найти в нашей статье в блоге Google Security .

Блокировка чтения из перекрестного источника

Даже если все межсайтовые страницы помещены в отдельные процессы, страницы все равно могут законно запрашивать некоторые межсайтовые подресурсы, такие как изображения и JavaScript. Вредоносная веб-страница может использовать элемент <img> для загрузки файла JSON с конфиденциальными данными, такими как ваш банковский баланс:

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

Без изоляции сайта содержимое файла JSON попадет в память процесса обработки, после чего средство визуализации заметит, что это недопустимый формат изображения, и не будет отображать изображение. Но затем злоумышленник может воспользоваться такой уязвимостью, как Spectre, чтобы потенциально прочитать этот фрагмент памяти.

Вместо использования <img> злоумышленник может также использовать <script> для сохранения конфиденциальных данных в памяти:

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

Блокировка чтения из разных источников, или CORB, — это новая функция безопасности, которая предотвращает попадание содержимого balance.json в память процесса рендеринга на основе его типа MIME.

Давайте разберемся, как работает CORB. Веб-сайт может запрашивать с сервера два типа ресурсов:

  1. ресурсы данных , такие как документы HTML, XML или JSON.
  2. медиа-ресурсы , такие как изображения, JavaScript, CSS или шрифты.

Веб-сайт может получать ресурсы данных из своего собственного источника или из других источников с помощью разрешающих заголовков CORS , таких как Access-Control-Allow-Origin: * . С другой стороны, медиа-ресурсы могут быть включены из любого источника, даже без разрешающих заголовков CORS.

CORB не позволяет процессу рендеринга получать ресурс данных из разных источников (т. е. HTML, XML или JSON), если:

  • ресурс имеет заголовок X-Content-Type-Options: nosniff
  • CORS явно не разрешает доступ к ресурсу

Если ресурс данных перекрестного происхождения не имеет установленного заголовка X-Content-Type-Options: nosniff , CORB пытается прослушать тело ответа, чтобы определить, является ли он HTML, XML или JSON. Это необходимо, поскольку некоторые веб-серверы неправильно настроены и, например, отображают изображения как text/html .

Ресурсы данных, заблокированные политикой CORB, представляются процессу как пустые, хотя запрос все равно выполняется в фоновом режиме. В результате вредоносной веб-странице сложно включить в свой процесс межсайтовые данные для кражи.

Для оптимальной безопасности и получения преимуществ от CORB мы рекомендуем следующее:

  • Отметьте ответы правильным заголовком Content-Type . (Например, ресурсы HTML должны предоставляться как text/html , ресурсы JSON — с типом JSON MIME , а ресурсы XML — с типом XML MIME ).
  • Откажитесь от прослушивания, используя заголовок X-Content-Type-Options: nosniff . Без этого заголовка Chrome выполняет быстрый анализ контента, чтобы попытаться подтвердить правильность типа, но поскольку это приводит к ошибке в разрешении ответов во избежание блокировки таких вещей, как файлы JavaScript, вам лучше утвердительно поступить правильно. сам.

Более подробную информацию можно найти в статье CORB для веб-разработчиков или в нашем подробном объяснении CORB .

Почему веб-разработчикам следует заботиться об изоляции сайтов?

По большей части изоляция сайта — это скрытая функция браузера, которая не доступна напрямую веб-разработчикам. Например, нет нового API, доступного в Интернете, для изучения. В общем, веб-страницы не должны различать работу с изоляцией сайта или без нее.

Однако из этого правила есть некоторые исключения. Включение изоляции сайта имеет несколько незначительных побочных эффектов, которые могут повлиять на ваш сайт. Мы ведем список известных проблем с изоляцией сайта и подробно останавливаемся на наиболее важных из них ниже.

Полностраничный макет больше не синхронен

При использовании изоляции сайта синхронность полностраничного макета больше не гарантируется, поскольку теперь фреймы страницы могут распределяться по нескольким процессам. Это может повлиять на страницы, если они предполагают, что изменение макета немедленно распространяется на все фреймы на странице.

В качестве примера давайте рассмотрим веб-сайт fluffykittens.example , который взаимодействует с социальным виджетом, размещенным на 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>

Сначала ширина <iframe> социального виджета составляет 123 пикселя. Но затем страница FluffyKittens меняет ширину на 456 пикселей (триггерный макет) и отправляет сообщение социальному виджету, который имеет следующий код:

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

Всякий раз, когда социальный виджет получает сообщение через API postMessage , он регистрирует ширину своего корневого элемента <html> .

Какое значение ширины регистрируется? До того, как Chrome включил изоляцию сайта, ответ был 456 . Доступ к document.documentElement.clientWidth приводит к принудительному макету, который раньше был синхронным до того, как Chrome включил изоляцию сайта. Однако, если включена изоляция сайта, изменение макета социального виджета между источниками теперь происходит асинхронно в отдельном процессе. Таким образом, ответ теперь также может быть 123 , то есть старое значение width .

Если страница меняет размер <iframe> из разных источников, а затем отправляет ему postMessage , при использовании изоляции сайта принимающий фрейм может еще не знать свой новый размер при получении сообщения. В более общем плане это может привести к поломке страниц, если они предполагают, что изменение макета немедленно распространяется на все фреймы на странице.

В этом конкретном примере более надежное решение могло бы установить width в родительском фрейме и обнаружить это изменение в <iframe> , прослушивая событие resize .

Обработчики выгрузки могут чаще выходить из строя

Когда фрейм перемещается или закрывается, старый документ, а также все документы подфрейма, встроенные в него, запускают свой обработчик unload . Если новая навигация происходит в том же процессе рендеринга (например, для навигации того же источника), обработчики unload старого документа и его подкадров могут работать сколь угодно долго, прежде чем разрешить фиксацию новой навигации.

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

В этой ситуации обработчики unload во всех кадрах очень надежны.

Однако даже без изоляции сайта некоторые переходы по основному фрейму являются межпроцессными, что влияет на поведение обработчика выгрузки. Например, если вы перейдете от old.example к new.example введя URL-адрес в адресной строке, навигация new.example произойдет в новом процессе. Обработчики выгрузки для old.example и его подкадров выполняются в процессе old.example в фоновом режиме после отображения страницы new.example , а старые обработчики выгрузки завершаются, если они не завершаются в течение определенного времени ожидания . Поскольку обработчики выгрузки могут не завершить работу до истечения времени ожидания, поведение выгрузки менее надежно.

Благодаря изоляции сайтов все переходы между сайтами становятся межпроцессными, поэтому документы с разных сайтов не используют общий процесс друг с другом. В результате описанная выше ситуация применяется в большем количестве случаев, а обработчики выгрузки в <iframe> часто имеют поведение фона и тайм-аута, описанное выше.

Еще одно отличие, возникающее в результате изоляции сайта, — это новый параллельный порядок обработчиков выгрузки: без изоляции сайта обработчики выгрузки выполняются в строгом порядке сверху вниз во всех кадрах. Но при использовании Site Isolation обработчики выгрузки выполняются параллельно в разных процессах.

Это фундаментальные последствия включения изоляции сайта. Команда Chrome работает над повышением надежности обработчиков выгрузки для распространенных случаев использования, где это возможно. Нам также известны ошибки, из-за которых обработчики выгрузки подкадра еще не могут использовать определенные функции, и работаем над их устранением.

Важным случаем для обработчиков выгрузки является отправка пингов конца сеанса. Обычно это делается следующим образом:

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

Лучшим и более надежным подходом в свете этого изменения является использование вместо этого navigator.sendBeacon :

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

Если вам нужен больший контроль над запросом, вы можете использовать опцию keepalive Fetch API:

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

Заключение

Изоляция сайтов затрудняет доступ ненадежных веб-сайтов к вашим учетным записям на других веб-сайтах или их кражу, поскольку каждый сайт изолируется в своем собственном процессе. В рамках этого CORB пытается не допускать использования ресурсов конфиденциальных данных в процессе рендеринга. Наши рекомендации, приведенные выше, помогут вам максимально эффективно использовать эти новые функции безопасности.

Спасибо Алексу Мощуку, Чарли Рейсу, Джейсону Миллеру, Наско Оскову, Филиппу Уолтону, Шубхи Паникеру и Томасу Штайнеру за прочтение черновой версии этой статьи и предоставление своих отзывов.