XSS при слабой CSP (unsafe-inline)
Reflected XSS эксплуатируется благодаря CSP с 'unsafe-inline'. Исправление требует nonce-based CSP.
hardgolangPro
Задача
# XSS при слабой Content-Security-Policy (unsafe-inline)
## Сценарий
Вы проводите аудит безопасности интернет-магазина **SecureShop**. Разработчик добавил заголовок `Content-Security-Policy` (CSP) с целью защиты от XSS — и считает задачу закрытой. Однако значение политики содержит `script-src 'unsafe-inline' 'self'`, что прямо разрешает выполнение inline-скриптов и сводит защиту от XSS к нулю. Параллельно в коде каталога есть кастомная функция шаблона, отключающая HTML-экранирование. У приложения есть бот-администратор, который проверяет жалобы через форму **«Report to Admin»** — он откроет любую переданную ссылку.
## Цель
1. Изучите CSP-заголовок ответа в Network → Headers и определите его слабость.
2. Найдите Reflected XSS в поиске каталога (`/catalog?q=...`).
3. Убедитесь, что `<script>` исполняется — несмотря на присутствие CSP-заголовка.
4. Сформируйте вредоносную ссылку, отправляющую `document.cookie` на ваш Exploit Server.
5. Через `/report` доставьте ссылку администратору, перехватите его сессию и заберите флаг с `/admin`.
## Теория
**Content-Security-Policy** — заголовок, ограничивающий, какие источники скриптов и стилей браузер имеет право исполнять. Грамотная CSP — мощный второй слой защиты от XSS: даже если в страницу проник `<script>`, браузер его не запустит. Но защита работает только при правильной политике. Директива `'unsafe-inline'` в `script-src` явно разрешает любые inline-скрипты, что **полностью отключает** CSP как XSS-mitigation. Современный безопасный подход — nonce-based CSP: на каждый запрос сервер генерирует уникальный токен (nonce) и помечает им только свои легитимные `<script nonce="...">`. Inline-скрипты без nonce браузер блокирует.
**Уязвимый паттерн (двойная ошибка):**
```go
// (1) Шаблонизатор отключает экранирование пользовательского ввода:
funcMap := template.FuncMap{
"marker": func(s string) template.HTML { return template.HTML(s) },
}
// шаблон: <p>Showing results for "{{marker .Query}}"</p>
// (2) CSP с unsafe-inline — никакой защиты второго слоя:
w.Header().Set("Content-Security-Policy",
"default-src 'self'; script-src 'unsafe-inline' 'self'; style-src 'self' 'unsafe-inline'")
```
## Точка входа атаки
| Параметр | Значение |
|----------|----------|
| Обычный пользователь | `demo` / `demo` |
| Уязвимый эндпоинт | `GET /catalog?q=...` (reflected XSS в поиске) |
| Доставка боту | `POST /report` (бот откроет URL) |
| Цель | `GET /admin` (требует роль `admin`) |
| Exploit Server | внешний URL для приёма украденных данных (см. панель лабы) |