Проблема
Команды часто проверяют API как набор URL, а не как модель владения объектами. В результате endpoint выглядит защищенным на уровне функции, но пропускает объект другого пользователя.
Сырые сканеры почти не понимают бизнес-смысл: они видят параметры id, userId, accountId, но не знают, кому объект принадлежит и какая роль должна его видеть.
Решение
Начинать нужно с inventory: HAR, OpenAPI, роли, object identifiers и список sensitive flows. После этого read-only replay сравнивает ответы роли A и роли B без изменения состояния.
Finding появляется только если есть role-differential evidence: статус, редирект, content-type, размер ответа, hash и безопасно редактированный фрагмент доказательства.
Что проверить руками
- Собрать HAR от двух тестовых аккаунтов: owner и viewer.
- Отметить endpoint-ы account, profile, billing, upload, admin, invite.
- Проверить только GET/HEAD/OPTIONS и зафиксировать, где роль B видит объект роли A.
- Не сохранять raw body с PII в отчёт, а хранить hash и редактированное evidence.
Безопасный пример кода
Read-only inventory extractor из HAR. Пример рассчитан на owned/lab-среду и не выполняет вредоносных действий.
import json
from pathlib import Path
from urllib.parse import urlsplit
OBJECT_KEYS = ("id", "userId", "accountId", "profileId", "invoiceId", "orderId")
def collect_operations(har_path: str) -> list[dict]:
har = json.loads(Path(har_path).read_text(encoding="utf-8"))
operations = []
for item in har.get("log", {}).get("entries", []):
req = item.get("request", {})
method = req.get("method", "GET").upper()
url = req.get("url", "")
parsed = urlsplit(url)
params = [p.get("name", "") for p in req.get("queryString", [])]
object_params = [name for name in params if name in OBJECT_KEYS or name.lower().endswith("id")]
operations.append({
"method": method,
"host": parsed.netloc,
"path": parsed.path,
"read_only": method in {"GET", "HEAD", "OPTIONS"},
"object_params": sorted(set(object_params)),
})
return operations
for op in collect_operations("owned-test-session.har"):
if op["read_only"] and op["object_params"]:
print(op)
Как это должно попасть в отчёт
- Role Matrix: owner/viewer/admin по каждому sensitive endpoint.
- Coverage: authenticated_not_configured, single_role_only, stateful_executed.
- Retest: тот же read-only запрос от роли B должен возвращать 403/404 или объект роли B.