Безопасное размещение пользовательских данных в современных веб-приложениях.

Дэвид Дворкен
David Dworken

Многим веб-приложениям необходимо отображать контент, контролируемый пользователем. Это может быть так же просто, как предоставление загруженных пользователем изображений (например, фотографий профиля), или столь же сложно, как рендеринг контролируемого пользователем HTML (например, учебник по веб-разработке). Это всегда было сложно сделать безопасно, поэтому мы постарались найти простые, но безопасные решения, которые можно применять к большинству типов веб-приложений.

Классические решения для изоляции ненадежного контента

Классическим решением для безопасного обслуживания контента, контролируемого пользователем, является использование так называемых доменов песочницы . Основная идея заключается в том, что если основной домен вашего приложения — example.com , вы можете размещать весь ненадежный контент на exampleusercontent.com . Поскольку эти два домена являются межсайтовыми , любой вредоносный контент на exampleusercontent.com не может повлиять на example.com .
Этот подход можно использовать для безопасного обслуживания всех видов ненадежного контента, включая изображения, загрузки и HTML. Хотя может показаться, что нет необходимости использовать это для изображений или загрузок, это помогает избежать рисков, связанных с перехватом контента , особенно в устаревших браузерах.
Домены-песочницы широко используются в отрасли и уже давно хорошо зарекомендовали себя. Но у них есть два существенных недостатка:

  • Приложениям часто необходимо ограничить доступ к контенту одному пользователю, что требует реализации аутентификации и авторизации. Поскольку домены песочницы намеренно не передают файлы cookie основному домену приложения, сделать это безопасно очень сложно. Чтобы поддерживать аутентификацию, сайты должны либо полагаться на функциональные URL-адреса , либо устанавливать отдельные файлы cookie аутентификации для домена песочницы. Этот второй метод особенно проблематичен в современной сети, где многие браузеры по умолчанию ограничивают межсайтовые файлы cookie.
  • Хотя пользовательский контент изолирован от основного сайта, он не изолирован от другого пользовательского контента. Это создает риск того, что вредоносный пользовательский контент атакует другие данные в домене песочницы (например, путем чтения данных того же происхождения).

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

Современные решения для обслуживания пользовательского контента

Со временем Интернет развился, и теперь появились более простые и безопасные способы предоставления ненадежного контента. Здесь существует много разных подходов, поэтому мы опишем два решения, которые сейчас широко используются в Google.

Подход 1. Обслуживание неактивного пользовательского контента.

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

  • Всегда устанавливайте заголовок Content-Type на общеизвестный тип MIME , который поддерживается всеми браузерами и гарантированно не содержит активного контента (в случае сомнений безопасным выбором является application/octet-stream ).
  • Кроме того, всегда устанавливайте приведенные ниже заголовки ответов, чтобы браузер полностью изолировал ответ.
Заголовок ответа Цель

X-Content-Type-Options: nosniff

Предотвращает перехват контента

Content-Disposition: attachment; filename="download"

Запускает загрузку, а не рендеринг

Content-Security-Policy: sandbox

Помещает контент в песочницу, как если бы он был размещен в отдельном домене.

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

Отключает выполнение JavaScript (и включение любых подресурсов)

Cross-Origin-Resource-Policy: same-site

Предотвращает межсайтовое включение страницы

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

Глубокоэшелонированная защита

Хотя приведенное выше решение в целом представляет собой достаточную защиту от XSS, существует ряд дополнительных мер по усилению защиты, которые вы можете применить для обеспечения дополнительных уровней безопасности:

  • Установите заголовок X-Content-Security-Policy: sandbox для совместимости с IE11.
  • Установите заголовок Content-Security-Policy: frame-ancestors 'none' чтобы заблокировать внедрение конечной точки.
  • Пользовательский контент песочницы на изолированном поддомене:
    • Обслуживание пользовательского контента на изолированном поддомене (например, Google использует такие домены, как product.usercontent.google.com ).
    • Установите Cross-Origin-Opener-Policy: same-origin и Cross-Origin-Embedder-Policy: require-corp чтобы включить изоляцию между источниками .

Подход 2. Обслуживание активного пользовательского контента.

Безопасное обслуживание активного контента (например, изображений HTML или SVG) также может осуществляться без недостатков классического подхода к домену-песочнице.
Самый простой вариант — воспользоваться заголовком Content-Security-Policy: sandbox чтобы сообщить браузеру изолировать ответ. Хотя не все веб-браузеры в настоящее время реализуют изоляцию процессов для документов «песочницы», постоянные усовершенствования моделей процессов браузера, вероятно, улучшат отделение содержимого «песочницы» от встраиваемых приложений. Если атаки SpectreJS и компрометации средств рендеринга выходят за рамки вашей модели угроз, то использование песочницы CSP, вероятно, будет достаточным решением.
В Google мы разработали решение, которое позволяет полностью изолировать ненадежный активный контент путем модернизации концепции доменов-песочниц. Основная идея заключается в том, чтобы:

  • Создайте новый домен песочницы, который будет добавлен в список общедоступных суффиксов . Например, добавив exampleusercontent.com в PSL, вы можете гарантировать, что foo.exampleusercontent.com и bar.exampleusercontent.com являются межсайтовыми и, таким образом, полностью изолированы друг от друга.
  • Все URL-адреса, соответствующие *.exampleusercontent.com/shim перенаправляются в статический файл оболочки. Этот файл оболочки содержит короткий фрагмент HTML и JavaScript, который прослушивает обработчик событий message и отображает любое полученное содержимое.
  • Чтобы использовать это, продукт создает iframe или всплывающее окно для $RANDOM_VALUE.exampleusercontent.com/shim и использует postMessage для отправки ненадежного контента в прокладку для рендеринга.
  • Отрисованный контент преобразуется в Blob и отображается внутри изолированного iframe .

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

Заключение

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