| Актив | Тип | Ценность | Описание |
|---|---|---|---|
| Данные пользователей | Данные | Высокая | Возможна утечка персональных данных и эксплуатация XSS |
| Данные пользовательских сессий | Данные | Высокая | Раскрывают поведенческие паттерны пользователей |
| Файловая система сервера | Инфраструктура | Критическая | Подвержена атаке Path Traversal |
| Внутренние сетевые ресурсы | Инфраструктура | Критическая | Могут быть скомпрометированы через SSRF |
| REST API | Приложение | Высокая | Основная точка взаимодействия с системой |
| Логи приложения | Данные | Средняя | Могут содержать чувствительную информацию |
| Категория | Применимость | Поверхность атаки | Потенциальный ущерб |
|---|---|---|---|
| Spoofing | Да | Все эндпоинты | Подмена пользователей |
| Tampering | Да | /register, /recordSession |
Изменение аналитических данных |
| Repudiation | Да | Отсутствие логирования | Невозможность доказать действия |
| Information Disclosure | Да | /userProfile, /exportReport, /notify |
Утечка конфиденциальных данных |
| Denial of Service | Да | Все эндпоинты | Перегрузка сервера |
| Elevation of Privilege | Да | /notify, /exportReport |
Доступ к внутренним ресурсам |
| Параметр | Значение |
|---|---|
| Компонент | /userProfile |
| CWE | CWE-79 |
| CVSS | 6.1 MEDIUM (AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N) |
| Критичность | Medium |
| Статус | Confirmed |
Описание:
Имя пользователя (userName) сохраняется в системе и затем отображается в HTML-ответе эндпоинта /userProfile без
экранирования. Это позволяет злоумышленнику внедрить вредоносный JavaScript-код, который будет выполняться в браузере
других пользователей. Проблема возникает из-за отсутствия HTML-экранирования пользовательского ввода.
Шаги воспроизведения:
- Зарегистрировать пользователя с вредоносным именем:
curl -X POST "http://localhost:7000/register?userId=xss&userName=<script>alert(1)</script>" - Открыть профиль пользователя:
curl "http://localhost:7000/userProfile?userId=xss" - Ожидаемый результат: HTML-теги экранированы, скрипт не выполняется.
Фактический результат: в ответе присутствует тег
<script>, который выполняется в браузере.
Влияние:
Атакующий может:
- Выполнять JavaScript-код в браузере жертвы.
- Похищать cookies и токены сессий.
- Выполнять действия от имени пользователя.
- Проводить фишинговые атаки.
Рекомендации по исправлению:
Экранировать пользовательский ввод перед выводом в HTML.
Security Test Case:
@Test
@DisplayName("[SECURITY] Stored XSS should be sanitized")
void storedXssShouldBeSanitized() {
String malicious = "<script>alert(1)</script>";
Unirest.post("http://localhost:7000/register")
.queryString("userId", "xss")
.queryString("userName", malicious)
.asString();
HttpResponse<String> response = Unirest.get("http://localhost:7000/userProfile")
.queryString("userId", "xss")
.asString();
assertEquals(200, response.getStatus());
assertFalse(response.getBody().contains("<script>"));
}| Параметр | Значение |
|---|---|
| Компонент | /exportReport |
| CWE | CWE-22 |
| CVSS | 9.1 CRITICAL (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) |
| Критичность | High |
| Статус | Confirmed |
Описание:
Эндпоинт /exportReport использует параметр filename для создания файла без проверки пути. Злоумышленник может
использовать последовательности ../ для выхода за пределы разрешённой директории и записи или перезаписи произвольных
файлов на сервере.
Шаги воспроизведения:
- Отправить запрос с вредоносным именем файла:
curl "http://localhost:7000/exportReport?userId=test&filename=../../../../tmp/pwned.txt" - Проверить наличие созданного файла вне директории reports.
- Ожидаемый результат: запрос отклонён с ошибкой 400. Фактический результат: файл создаётся за пределами разрешённой директории.
Влияние:
Атакующий может:
- Записывать или перезаписывать системные файлы.
- Размещать вредоносные скрипты.
- Получать доступ к конфиденциальной информации.
- Потенциально добиться удалённого выполнения кода.
Рекомендации по исправлению:
Нормализовать путь и проверить его принадлежность базовой директории.
Security Test Case:
@Test
@DisplayName("[SECURITY] Path traversal should be prevented")
void pathTraversalShouldBePrevented() {
String maliciousFilename = "../../etc/passwd";
HttpResponse<String> response = Unirest.get("http://localhost:7000/exportReport")
.queryString("userId", "test")
.queryString("filename", maliciousFilename)
.asString();
assertEquals(400, response.getStatus());
}| Параметр | Значение |
|---|---|
| Компонент | /notify |
| CWE | CWE-918 |
| CVSS | 9.1 CRITICAL (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:L/A:N) |
| Критичность | Critical |
| Статус | Confirmed |
Описание:
Эндпоинт /notify принимает параметр callbackUrl и выполняет HTTP-запрос к указанному адресу без проверки. Это
позволяет злоумышленнику инициировать запросы к внутренним сервисам или метаданным облачной инфраструктуры.
Шаги воспроизведения:
- Отправить запрос к внутреннему ресурсу:
curl -X POST "http://localhost:7000/notify?userId=test&callbackUrl=http://169.254.169.254/latest/meta-data/" - Проанализировать ответ сервера.
- Ожидаемый результат: Запрос отклонён с ошибкой 400. Фактический результат: Сервер выполняет запрос к внутреннему ресурсу.
Влияние:
Атакующий может:
- Получать доступ к внутренним сервисам.
- Извлекать метаданные облачной инфраструктуры.
- Обходить сетевые ограничения.
- Использовать сервер для сканирования внутренней сети.
Рекомендации по исправлению:
Разрешить только HTTPS-запросы и использовать белый список доменов.
Security Test Case:
@Test
@DisplayName("[SECURITY] SSRF should be blocked")
void ssrfShouldBeBlocked() {
String maliciousUrl = "http://127.0.0.1:8080";
HttpResponse<String> response = Unirest.post("http://localhost:7000/notify")
.queryString("userId", "test")
.queryString("callbackUrl", maliciousUrl)
.asString();
assertEquals(400, response.getStatus());
}| Параметр | Значение |
|---|---|
| Компонент | Все эндпоинты |
| CWE | CWE-306 |
| CVSS | 9.1 CRITICAL (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N) |
| Критичность | High |
| Статус | Confirmed |
Описание:
Все эндпоинты API доступны без аутентификации и авторизации. Любой пользователь может выполнять операции от имени
другого, передавая произвольный userId. Это приводит к нарушению конфиденциальности и целостности данных.
Шаги воспроизведения:
- Зарегистрировать пользователя:
curl -X POST "http://localhost:7000/register?userId=user1&userName=User1" - Получить данные другого пользователя без аутентификации:
curl "http://localhost:7000/userProfile?userId=user1" - Ожидаемый результат: Доступ запрещён (401/403) без аутентификации. Фактический результат: Данные пользователя возвращаются без каких-либо проверок.
Влияние:
Атакующий может:
- Получать доступ к данным других пользователей.
- Изменять или удалять данные.
- Выполнять действия от имени жертвы.
- Компрометировать всю систему.
Рекомендации по исправлению:
Внедрить аутентификацию.
Security Test Case:
@Test
@DisplayName("[SECURITY] Access should require authentication")
void accessShouldRequireAuthentication() {
HttpResponse<String> response = Unirest.get("http://localhost:7000/userProfile")
.queryString("userId", "user1")
.asString();
assertEquals(401, response.getStatus());
}| Параметр | Значение |
|---|---|
| Компонент | Все эндпоинты |
| CWE | CWE-639 |
| CVSS | 8.1 HIGH (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N) |
| Критичность | High |
| Статус | Confirmed |
Описание:
Даже при наличии аутентификации, отсутствие проверки соответствия userId аутентифицированному пользователю позволяет
получать доступ к данным других пользователей. Это классическая уязвимость IDOR.
Шаги воспроизведения:
- Получить JWT-токен для пользователя
user1. - Выполнить запрос к данным пользователя
user2:curl -H "Authorization: Bearer <token_user1>" \ "http://localhost:7000/userProfile?userId=user2" - Ожидаемый результат: Ответ 403 Forbidden.
Фактический результат: Возвращаются данные пользователя
user2.
Влияние:
Атакующий может:
- Получать конфиденциальные данные других пользователей.
- Нарушать целостность и конфиденциальность системы.
- Выполнять действия от имени других пользователей.
Рекомендации по исправлению:
Проверять, что userId из токена совпадает с userId в запросе:
Security Test Case:
@Test
@DisplayName("[SECURITY] IDOR should be prevented")
void idorShouldBePrevented() {
String tokenUser1 = "validTokenForUser1";
HttpResponse<String> response = Unirest.get("http://localhost:7000/userProfile")
.header("Authorization", "Bearer " + tokenUser1)
.queryString("userId", "user2")
.asString();
assertEquals(403, response.getStatus());
}