Bug2Lab · Secure Code · Java / Spring MVC

Reflected XSS в сообщении об ошибке

Пользователь отправляет во входные данные фрагмент HTML/JS. Сервер возвращает это прямо в страницу ошибки без экранирования — скрипт выполняется в браузере другого пользователя. В этой лабораторной мы фиксируем правило «никогда не печатай сырые данные пользователя в HTML».

1
Как выглядит атака
Злоумышленник отправляет: name=<script>alert('XSS')</script>. Ответ страницы рендерит это прямо в HTML. В итоге чужой браузер выполняет внедрённый JS.
POST /feedback
400 Bad Request
<div class="error">
 Неверное имя:  <script>alert('XSS')</script>
</div>
2
Почему это стало возможным
Код просто вставляет параметр пользователя в HTML без экранирования. То есть мы доверяем тому, что нам прислал клиент — и печатаем это как есть.
Проблемный код
52 @PostMapping("/feedback")
53 public String sendFeedback(@RequestParam String name, Model model){
54 if(name.isBlank()){
55 model.addAttribute("error","Неверное имя: " + name); // ❌ сырое вставление
56 return "errorPage";
57 }
58 // ...
59 return "ok";
60 }
3
Как это чинится правильно
Любой пользовательский ввод выводится только через безопасный биндинг/экранирование в шаблоне. Никакой «строкой склеили HTML». Мы не конкатим теги руками.
Исправленный паттерн
52 @PostMapping("/feedback")
53 public String sendFeedback(@RequestParam String name, Model model){
54 if(name.isBlank()){
55 model.addAttribute("errorName", name); // ✅ храним отдельно
56 return "errorPageSafe"; // шаблон сам экранирует ${errorName}
57 }
58 return "ok";
59 }

bug2regress Регрессионный тест (стоп-кран)

Мы шлём валидационную ошибку с пейлоадом <script>alert('XSS')</script> и проверяем, что в ответе нет сырых тегов <script>. Если теги вернулись в тело ответа — релиз стопаем.

#!/bin/bash
# xss-check.sh
PAYLOAD="<script>alert('XSS')</script>"

RESP=$(curl -s -X POST \
  -d "name=$PAYLOAD" \
  https://staging.example.internal/feedback)

echo "$RESP" | grep "<script>" && {
  echo "[BLOCK] Reflected XSS: сырые теги в ответе"
  exit 1
}

echo "[OK] Пользовательский ввод экранируется"
exit 0

Это гарантирует, что "вернули сырое значение пользователя в HTML" больше не доедет до продакшена молча.

Проверка понимания Что пошло не так?

Стандарт после лабораторной: любой пользовательский ввод на странице отображается ТОЛЬКО через безопасный биндинг (экранирование). Никогда — через ручную конкатенацию строк.

Теория (по желанию) Чем опасен XSS и почему «просто alert» — не смешно