в которой пользовательский контент будет изолирован от вашего основного сайта и от других пользователей. Это классическая архитектурная проблема, и решается она на нескольких уровнях: от HTML-атрибутов до серверных заголовков.
Вот как правильно выстроить такую защиту.
1. Защита вашего конструктора от встраивания (защита от кликджекинга)
Прежде всего, вы должны запретить другим сайтам встраивать ваш конструктор в свои iframe. Если злоумышленник сможет встроить вашу страницу, он сможет атаковать ваших пользователей. Для этого на сервере, который отдает страницу конструктора, нужно настроить HTTP-заголовки:
- X-Frame-Options: SAMEORIGIN — разрешает встраивать ваш конструктор только на ваших же доменах.
- Content-Security-Policy: frame-ancestors 'self' — современный аналог, который делает то же самое, но поддерживается шире. Если вы хотите разрешить встраивание только на конкретном партнерском сайте, укажите его:
frame-ancestors 'self' partner-site.com.3 источника
2. Изоляция пользовательского iframe (атрибут sandbox)
Когда вы встраиваете iframe пользователя внутрь своего конструктора, используйте атрибут sandbox без каких-либо разрешений. Это самый строгий режим:
<iframe src="user-content.html" sandbox></iframe>
В этом режиме браузер полностью блокирует для вложенного фрейма:
- выполнение любых скриптов (защита от XSS);
- отправку форм;
- использование API (localStorage, IndexedDB, геолокация);
- выполнение плагинов;
- изменение
window.top (защита от атак типа frame busting); - доступ к кукам и данным родительского окна.
Важное предупреждение: Если пользовательский контент находится с вами на одном домене (same-origin) и вы добавите allow-same-origin вместе с allow-scripts, встроенный код сможет программно удалить атрибут sandbox со своего фрейма, и вся защита исчезнет. Избегайте этой комбинации для недоверенного кода.
3. Точечное включение функционала (allow)
Если пользователю для работы конструктора что-то нужно (например, видео с YouTube или отправка формы), вы по одному добавляете разрешения в атрибут sandbox. Принцип минимальных привилегий:
allow-scripts — разрешает JavaScript (но без allow-same-origin скрипты не смогут снять песочницу).allow-forms — разрешает отправку форм.allow-same-origin — опасный флаг. Разрешает контенту обращаться к вашим кукам и API как к родному домену. Включайте только если контент действительно с того же origin.allow-popups — разрешает открывать новые окна.allow-downloads — разрешает скачивание файлов.allow-top-navigation — позволяет фрейму менять URL всей страницы (обычно не нужно).allow-storage-access-by-user-activation — дает доступ к хранилищу после действия пользователя.
Пример для конструктора, где пользователю нужен только JS и формы:
<iframe src="user-widget.html" sandbox="allow-scripts allow-forms"></iframe>
4. Защита на уровне CSP (Content Security Policy)
На сервере вашего конструктора (внутренней страницы) задайте политику для вложенных фреймов. Это страховка на случай, если вы ошибетесь в HTML:
Content-Security-Policy: frame-src 'self' https://trusted-user-origins.com;
Это заставит браузер блокировать любые iframe, источники которых не указаны в списке, даже если HTML-код страницы будет скомпрометирован.
5. Дополнительные меры безопасности
- Referrer-Policy: Установите
referrer-origin-when-cross-origin или no-referrer на уровне конструктора, чтобы пользовательский iframe не получил полный URL и параметры вашей родительской страницы. - Загрузка: Добавьте
loading="lazy" к iframe, чтобы не замедлять отрисовку конструктора, если пользовательских виджетов много. - Проксирование: Если пользовательский контент потенциально опасен (например, может содержать тяжелый JS, майнеры или пытаться делать DDoS-запросы), не отдавайте его напрямую. Поднимите серверный прокси, который будет забирать контент пользователя, вырезать из него теги
<script>, <iframe> и on*-атрибуты, и только потом отдавать в ваш iframe. - Собственный origin: В идеале каждый пользовательский виджет должен отдаваться с уникального поддомена (например,
user123.your-platform.com). Тогда браузер изолирует его через Same-Origin Policy, и даже при наличии allow-same-origin виджет одного пользователя не сможет прочитать данные виджета другого.
Итоговая схема
- Ваша страница конструктора: заголовки
X-Frame-Options: SAMEORIGIN и CSP: frame-ancestors 'self'. - Внутренняя страница конструктора (куда вставляется пользователь): заголовок
CSP: frame-src 'self' .... - HTML-код:
<iframe src="путь-к-контенту-пользователя" sandbox="allow-scripts allow-forms" referrerpolicy="no-referrer"></iframe>.
Такая связка гарантирует, что даже если пользователь встроит в свой виджет вредоносный скрипт, он останется внутри его фрейма, не сможет украсть данные ваших клиентов, прочитать данные других виджетов или заблокировать работу вашего конструктора.