Межсайтовый скриптинг и классы уязвимостей на стороне браузера — Примеры

Пример 1 — Экранирование в точке вывода

Страница профиля показывает отображаемое имя участника в тексте и, ниже, в ссылке поиска. Компонент ниже был принят как исправление безопасности, потому что разработчик убрал prop сырого HTML. Перепишите ссылку так, чтобы значение кодировалось под контекст, в который попадает, и скажите, что ещё должно произойти там, где богатый текст действительно нужен.

// Profile.tsx — как на ревью
<p>{user.displayName}</p>
<a href={"/search?q=" + user.displayName}>Search</a>

Решение

// Profile.tsx — после исправления
<p>{user.displayName}</p>
<a href={"/search?q=" + encodeURIComponent(user.displayName)}>Search</a>

JSX в абзаце уже экранирует текст, но атрибут — другой контекст: без encodeURIComponent имя, содержащее & или #, меняет URL, а ломающий атрибут символ вырывается из ссылки. Обычная ошибка — считать, что «React экранирует по умолчанию» покрывает каждую привязку: каждый чёрный ход, включая prop сырого HTML, URL или значение стиля, требует кодирования под своё назначение. Там где богатый текст действительно необходим, санируйте его allow-list на сервере.

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

HTTP/1.1 200 OK
Set-Cookie: session=<opaque value>; Path=/

Решение

HTTP/1.1 200 OK
Set-Cookie: session=<opaque value>; Secure; HttpOnly; SameSite=Lax; Path=/

HttpOnly скрывает значение от JavaScript, поэтому удачная инъекция не может украсть сессию; Secure не пускает её на открытый канал; а SameSite=Lax блокирует большинство межсайтовых POST, оставляя навигацию верхнего уровня работающей, — поэтому Lax обычно предпочитают Strict. Значение записано как плейсхолдер; никогда не коммитьте и не вставляйте настоящее. Обычная ошибка — добавить один Secure и оставить cookie читаемой: это защищает провод, но не сессию.