Большинство руководств по пентесту API разбивают список OWASP на одиннадцать несвязанных задач. Но такой порядок упускает самые быстрые проверки. Вторая identity и необработанный JSON обычно выявляют ошибки авторизации раньше, чем поиск payload.
И тестируйте только staging или намеренно уязвимый lab API с письменным разрешением. Вам нужны две тестовые identity с разными владельцами или ролями, действительные токены, Bash с curl и jq, а также согласованный предел количества запросов и условие остановки. Burp Suite полезен для перехвата и повторной отправки; OWASP ZAP — разумный бесплатный вариант для автоматизированного baseline-сканирования.
Начните с lab API по адресу https://api.shop.test. Фикстура предоставляет Alice заказ 1001, а Bob — заказ 1002. Используйте эквивалентные тестовые данные в своей среде. Мы обнаружим маршруты, повторно отправим объекты от имени разных identity, проверим токены и поля, измерим лимиты, протестируем бизнес-процессы и проследим пользовательские URL. По состоянию на 15 августа 2026 года OWASP API Security Top 10 остаётся редакцией 2023 года; используйте её для маркировки находок, но приоритет в порядке тестирования отдавайте границам identity и авторизации.
В этой статье
- Область тестирования — это контроль, а не дисклеймер
- Составьте инвентаризацию маршрутов до проверки контролей
- Две identity выявляют авторизацию объектов
- Рассматривайте токены как входные данные, а не как факты
- Сравните возвращаемые поля с доступными для записи
- Измерьте лимиты и протестируйте identity, которой они доверяют
- Отделяйте доступ к функциям от злоупотребления бизнес-процессами
- Проследите значения до предполагаемых sink
- Отчёт должен содержать запрос, ответ и изменение состояния
Область тестирования — это контроль, а не дисклеймер
Зафиксируйте целевые hosts и исключённые пути. Укажите тестовые учётные записи и правила работы с данными. Добавьте часы тестирования, ограничения частоты запросов и контактное лицо на случай некорректного поведения сервиса. Заранее решите, что останавливает тест: рост частоты ошибок, увеличение очереди, неожиданное раскрытие данных или любой production-трафик.
Настройте harness. Переменная proxy направляет трафик командной строки через Burp или другой одобренный interceptor. Импортируйте его CA только в соответствии с одобренной настройкой вашей lab.
export HTTPS_PROXY=http://127.0.0.1:8080
BASE=https://api.shop.test
TOKEN_A='alice-test-token'
TOKEN_B='bob-test-token'
Значения ниже обозначают авторизованную цель. OWASP’s API Security Project предоставляет framework рисков; он не заменяет evidence запросов и ответов. Начните с BOLA, broken authentication, BOPLA и BFLA. Эти контроли устанавливают, кто может получить доступ к объекту, стать identity, изменить свойство или вызвать функцию.
Составьте инвентаризацию маршрутов до проверки контролей
Список маршрутов SPA — это источник для discovery, а не граница покрытия. Проверьте распространённые спецификации и metadata identity. Что раскрывает API?
for p in swagger-ui api-docs v3/api-docs openapi.json \
.well-known/openid-configuration; do
curl -s -o /dev/null -w "%{http_code} $p\n" "$BASE/$p"
done
Если документ OpenAPI защищён, повторите запрос с одобренным тестовым токеном. 401 означает «требуется authentication». Документация всё ещё может быть доступна.
curl -fsS "$BASE/openapi.json" |
jq -er 'if (.paths | type) == "object" then .paths | keys[] else error("no object paths") end' \
> endpoints.txt
Здесь -f отклоняет HTTP-ошибки, а -e заставляет команду завершиться с ошибкой при null-результате. Некорректный документ уже приводит к ошибке jq.
Для доступного staging frontend извлеките похожие на API URL из JavaScript:
katana -u https://app.shop.test -jc -silent |
grep -E '/api|/v[0-9]' |
sort -u > discovered-urls.txt
Храните полные URL отдельно от относительных путей OpenAPI. Инструментам вроде nuclei нужны доступные URL:
nuclei -l discovered-urls.txt -t http/exposures/
Проверьте старые версии, используя prefix, который фактически применяет ваш API:
for v in v1 v2 v3 internal beta; do
curl -s -o /dev/null -w "%{http_code} /api/$v/users\n" \
"$BASE/api/$v/users"
done
Успешный ответ документации помогает найти направления для проверки. Он не определяет их severity. Отдельно зафиксируйте открытые docs, устаревшие маршруты, stack traces и secrets на стороне клиента. Ограничьте production-документацию, выведите старые версии из эксплуатации, отключите traces и удалите поставленные вместе с приложением ключи.
Если инвентаризация выявляет /api/orders/{id}, начните с него.
Две identity выявляют авторизацию объектов
Повторно отправьте объект от имени неправильной identity
BOLA — самая быстрая полезная демонстрация. Сохраняйте метод, форму пути, headers и токен неизменными, меняя только ID объекта. Затем выполните проверку в обратном направлении для обеих identity.
Alice читает свой заказ:
curl -s "$BASE/api/orders/1001" \
-H "Authorization: Bearer $TOKEN_A" |
jq .
Теперь Alice запрашивает заказ Bob:
curl -i -s "$BASE/api/orders/1002" \
-H "Authorization: Bearer $TOKEN_A"
403 или нераскрывающий информацию 404 могут быть корректными. Важнее ownership и возвращаемые данные, чем один только status. Ответ, содержащий заказ Bob, или изменение его состояния — это находка.
Повторите проверку в обратном направлении:
curl -i -s "$BASE/api/orders/1001" \
-H "Authorization: Bearer $TOKEN_B"
UUID усложняют угадывание идентификаторов, но не обеспечивают authorization. Тестируйте идентификаторы в путях, query strings, JSON bodies, headers, nested resources, batch arrays, exports, downloads и в каждом поддерживаемом методе. Проверка чтения может существовать, тогда как update или delete остаётся открытым.
Повторите проверку для разных расположений и методов
Используйте ffuf только с известными тестовыми ID и низкой частотой запросов:
ffuf -u "$BASE/api/orders/FUZZ" \
-w <(printf '%s\n' 1001 1002) \
-H "Authorization: Bearer $TOKEN_A" \
-mc 200,403,404 \
-rate 1
Зелёный результат scanner в этом случае почти бесполезен. Сохраните исходный запрос и replay. Зафиксируйте различия между ответами. Отметьте затронутую границу identity и любое изменение состояния. Это и есть evidence.
Рассматривайте токены как входные данные, а не как факты
Читаемый JWT доказывает encoding. Он ничего не говорит о проверке подписи или validation claims.
Этот decoder обрабатывает распространённое дополнение base64url:
JWT="$TOKEN_A"
python3 - "$JWT" <<'PY'
import base64, json, sys
for part in sys.argv[1].split(".")[:2]:
part += "=" * (-len(part) % 4)
print(json.dumps(
json.loads(base64.urlsafe_b64decode(part)),
indent=2
))
PY
Проверьте alg, kid, iss, aud, sub, роли и срок действия. Основным тестом является ручной replay claims. Для версий, поддерживающих эти режимы, jwt_tool может создавать варианты:
jwt_tool --help
jwt_tool "$JWT" -T
jwt_tool "$JWT" -X a
Сгенерированный вариант — всего лишь тестовый артефакт. Сообщайте об algorithm confusion или принятии alg:none только в том случае, если сервер принимает токен и меняет доступ.
В lab, использующей выданные для тестов токены и тестовые ключи, можно проверить слабые HMAC secrets:
jwt_tool "$JWT" -C -d /usr/share/wordlists/rockyou.txt
Для проверки ссылки на ключ используйте только одобренный источник ключа в lab:
jwt_tool "$JWT" -X k -pk ./lab-issuer-public.pem
Проверьте изменённые payload, недействительные подписи, просроченные токены, неправильные aud или iss, replay после logout и повторное использование refresh token. Само наличие kid, jku или x5u не является находкой. Проверьте, остаются ли такие ссылки ограниченными доверенными ключами, и выполняйте запросы только внутри lab.
Исправление начинается с allowlist алгоритмов, а также проверки подписи и claims. Ограничьте ссылки на ключи. Выполняйте rotation refresh tokens, отзывайте их и выявляйте повторное использование. Слабое место этого метода — асинхронное поведение: curl может показать успешную постановку в очередь, тогда как опасная работа выполняется позже. Подтвердите такие случаи с помощью логов или владельца; не делайте вывод об impact только на основании HTTP-ответа.
Сравните возвращаемые поля с доступными для записи
BOPLA охватывает чтение и запись:
| Направление | Конкретное evidence | Исправление |
|---|---|---|
| Раскрытие данных при чтении | Response, соответствующий роли, содержит поле, которое должен исключать | Возвращайте явный response DTO |
| Overposting при записи | Последующий GET показывает, что isAdmin, ownership или другое защищённое значение изменилось | Применяйте server-side allowlist доступных для записи полей |
Изучите полный response Alice, замаскировав secrets:
curl -s "$BASE/api/users/me" \
-H "Authorization: Bearer $TOKEN_A" |
jq .
Проверяйте по одному полю за раз, используя disposable data. После каждой mutation сбрасывайте lab account:
curl -i -s -X PATCH "$BASE/api/users/me" \
-H "Authorization: Bearer $TOKEN_A" \
-H "Content-Type: application/json" \
-d '{"verified":true}'
Только если это разрешено тестовой средой, попробуйте защищённые поля, такие как role, isAdmin, ownerId или balance. Возвращённое в response поле — это направление для проверки. Последующее чтение, показывающее изменённые privilege или ownership, — это evidence.
Arjun может предложить JSON-параметры, хотя обработка body зависит от версии:
arjun -u "$BASE/api/users/me" \
-m JSON \
-H "Authorization: Bearer $TOKEN_A"
Измерьте лимиты и протестируйте identity, которой они доверяют
Зелёный результат scanner в этом случае почти бесполезен. Rate limiting — это вопрос identity и контроля затрат, а не число запросов из одного source address.
Отправьте ограниченный тест login:
for i in $(seq 1 20); do
curl -s -o /dev/null -w "$i %{http_code}\n" \
-X POST "$BASE/api/login" \
-H "Content-Type: application/json" \
-d "{\"user\":\"alice-test\",\"pass\":\"wrong-$i\"}"
sleep 0.2
done
curl -s -D - -o /dev/null \
-X POST "$BASE/api/login" \
-H "Content-Type: application/json" \
-d '{"user":"alice-test","pass":"wrong-final"}'
Если запросы 1–8 возвращают 401, а запрос 9 — 429, зафиксируйте окно из восьми попыток. Повторите после сброса и запишите Retry-After, если он присутствует.
Сохраняйте token, path, payload, timing и количество запросов неизменными, сравнивая baseline и forwarded headers:
for mode in baseline rotated; do
printf '%s: ' "$mode"
for i in $(seq 1 5); do
args=()
[ "$mode" = rotated ] &&
args=(-H "X-Forwarded-For: 192.0.2.$i")
curl -s -o /dev/null -w "%{http_code} " \
"$BASE/api/search?q=test" "${args[@]}"
done
printf '\n'
done
Большое количество успешных запросов при rotation предполагает, что spoofable header влияет на лимит. Само по себе это не доказывает bypass.
Проверьте pagination и ресурсоёмкие маршруты с небольшими запросами:
for q in 'limit=9999999' 'per_page=100000' 'page_size=-1'; do
curl -s "$BASE/api/items?$q" -o /dev/null \
-w "$q status=%{http_code} bytes=%{size_download}\n"
done
Тестируйте exports, отправку OTP, batch routes, а также глубину или batching GraphQL только в том случае, если они входят в scope. Остановитесь на согласованном пределе.
Для coupon lab API без списания средств проверяйте состояние, а не нагрузку:
for i in $(seq 1 3); do
curl -s -X POST "$BASE/api/apply-coupon" \
-H "Authorization: Bearer $TOKEN_A" \
-H "Content-Type: application/json" \
-d '{"code":"SAVE50"}'
printf '\n'
done
Первое применение должно изменить тестовую корзину. Последующие попытки должны быть отклонены или не менять её.
Отделяйте доступ к функциям от злоупотребления бизнес-процессами
Обычный token Alice не должен получать доступ к административным данным:
curl -i -s "$BASE/api/admin/users" \
-H "Authorization: Bearer $TOKEN_A"
Тестируйте задокументированные методы, версии, bulk routes и любое поведение method-override:
curl -i -s -X POST "$BASE/api/orders/1001" \
-H "Authorization: Bearer $TOKEN_A" \
-H "X-HTTP-Method-Override: DELETE"
Сравните response и состояние с обычным запросом, используя disposable data. Для каждого метода и версии необходима authorization по принципу default-deny.
Затем протестируйте чувствительные flows с небольшим количеством запросов. Повторите приведённый выше запрос coupon, а если инвентаризация содержит referral или quantity controls, проверьте их с disposable accounts. Для /api/checkout используйте только товары без списания средств и небольшое согласованное количество запросов. Успешный status важен, когда он доказывает несанкционированный переход состояния. Он также может показать повторную скидку или нарушение ограничения количества.
Проследите значения до предполагаемых sink
Рассматривать injection как одну строку SQLi — ленивое тестирование. Сначала выясните, куда попадает каждое контролируемое пользователем значение, затем используйте безвредные probes и подтвердите sink.
| Input | Предполагаемый sink | Безопасный probe | Evidence |
|---|---|---|---|
sort или filter | Database или search parser | Переключайтесь между задокументированными значениями | Reflection, ошибка parser или контролируемое изменение результата |
| Поле, похожее на template | Template renderer | Отправьте уникальный инертный marker | Неожиданный server-side rendering |
| JSON или XML field | Parser или downstream service | Добавьте одно безвредное неизвестное поле | Поведение parser или downstream |
url или callback_url | Server-side HTTP client | Отправьте одобренный callback URL | Metadata out-of-band запроса |
Endpoint lab /api/fetch принимает URL:
curl -i -s -X POST "$BASE/api/fetch" \
-H "Authorization: Bearer $TOKEN_A" \
-H "Content-Type: application/json" \
-d '{"url":"<YOUR_CONTROLLED_CALLBACK_URL>"}'
Blind fetch может не вернуть полезное body; evidence предоставляет callback observer. Зафиксируйте timestamp, source address, method и path.
Затем протестируйте контролируемый internal service:
curl -i -s -X POST "$BASE/api/fetch" \
-H "Authorization: Bearer $TOKEN_A" \
-H "Content-Type: application/json" \
-d '{"url":"http://127.0.0.1:8080/health"}'
Response от lab service убедительно свидетельствует, что fetch path достиг его. Подтвердите origin с помощью service logs или callback data. Используйте альтернативные представления loopback только в контролируемой staging network и никогда не обращайтесь к production internal addresses. Не запрашивайте cloud metadata credentials; для подтверждения SSRF path достаточно доказать контролируемую доступность internal service.
Если инвентаризация содержит source_url, link, image_url или webhook, примените тот же процесс. Для гипотез SQL, NoSQL, command, template и parser фиксируйте reflection, errors, timing и downstream behavior. Не заявляйте о наличии sink только на основании странной ошибки.
Отчёт должен содержать запрос, ответ и изменение состояния
Чек-лист находки
Используйте labels редакции 2023 года. BOPLA — это A03, unrestricted resource consumption — A04, sensitive business flows — A06, а SSRF — A07. Классификация OWASP сама по себе не определяет severity.
Перед завершением assessment проверьте:
- endpoint и method
- использованные identity и role
- исходный и повторно отправленный request
- response и существенное различие в JSON
- затронутые data или state
- OWASP ID и remediation
- условие retest
Чек-лист обеспечивает покрытие. Тройка request/response/state предоставляет вам finding.