Авторизация в API и приложении: как не открыть чужие данные
Разбор реальных векторов атак, архитектурных моделей и практического чек-листа для команд, которые хотят закрыть дыры до инцидента, а не после
Введение: почему авторизация — это не та задача, которую можно решить один раз
Разработчики часто путают аутентификацию и авторизацию. Аутентификация отвечает на вопрос "кто ты?", авторизация — на вопрос "что тебе разрешено?" Первую задачу принято решать через стандартные механизмы: пароль, 2FA, OAuth, SSO. Вторую — через собственную логику приложения. И именно здесь всё идёт наперекосяк [6].
Проблема в том, что авторизация — распределённая ответственность. В монолите это один слой middleware; в микросервисах — отдельная логика в каждом сервисе; в API-first-архитектуре — контроль на каждом эндпоинте, для каждого метода, с учётом роли, владельца объекта и контекста запроса. Стоит пропустить одну проверку — и пользователь видит чужие данные, меняет чужой аккаунт или вызывает административную функцию.
Для CTO и Tech Lead это важно по двум причинам. Первая — регуляторная: в России с 2025 года вступил в силу №420-ФЗ, который вводит штрафы за утечку персональных данных до 500 млн рублей [7]. Вторая — финансовая: по данным IBM Cost of Data Breach Report (2024), утечки, связанные с компрометацией учётных данных, обходятся организациям в среднем в 4,88 млн долларов за инцидент [5].
Эта статья — практический разбор того, где именно ломается авторизация, почему, и как выстроить защиту до инцидента, а не после постмортема.
Часть 1. Три главных класса авторизационных уязвимостей в API
BOLA / IDOR: "я меняю ID — и вижу чужое"
BOLA (Broken Object Level Authorization) — в контексте веба известная как IDOR (Insecure Direct Object Reference) — занимает первое место в OWASP API Security Top 10 2023 и остаётся одной из наиболее эксплуатируемых уязвимостей [8].
Механика проста. Приложение использует идентификатор ресурса (ID пользователя, номер заказа, UUID документа), переданный клиентом, и обращается к базе данных напрямую — без проверки, принадлежит ли этот объект вызывающему пользователю. Атакующий меняет user_id=123 на user_id=124 в URL, теле запроса или заголовке — и получает данные другого пользователя [9].
Идентификаторы сегодня появляются в самых разных местах запроса:
- URL-параметры и path-сегменты (
/api/orders/5678) - Query string (
?account=987) - JSON-тело запроса (
{"document_id": "abc123"}) - HTTP-заголовки (
X-User-Id)
BOLA особенно опасна в нескольких архитектурных сценариях [9]:
- REST API с конвенцией
/resource/{id}— структурная уязвимость "по умолчанию", если не реализованы ownership-проверки. - GraphQL API — argument-based object access создаёт ту же уязвимость, если resolver-ы пропускают authorization checks.
- Multi-tenant SaaS — пользователь одного тенанта манипулирует ID и обращается к данным другого.
- E-commerce — ID заказов, инвойсов, аккаунтов являются высокоценными целями.
- Healthcare — CVE-2024-28320 в Hospital Management System (CVSS 7.6) позволял читать и изменять данные пациентов [9].
Реальные кейсы показывают, что уязвимость не теоретическая. В 2019 году Instagram исправил IDOR, позволявший просматривать приватные посты и истории через манипуляцию user ID [2]. IDOR-цепочка в ZITADEL (CVE-2025-27507, CVSS 9.0) затрагивала интерфейс администрирования — один из самых критичных узлов любого identity-провайдера [10].
Корневая причина почти всегда одна: разработчик считает, что "раз пользователь авторизован — значит, он может делать запросы к этому эндпоинту". Это ошибка. Авторизованность не означает права на конкретный объект.
BFLA: "я вызываю функцию, которую не должен видеть"
Broken Function Level Authorization (BFLA) — пятый по критичности риск в OWASP API Security Top 10 2023 — работает по другому принципу: атакующий не меняет ID объекта, а вызывает функцию (эндпоинт), к которой у него не должно быть доступа [11].
Классический сценарий: приложение скрывает кнопку "Удалить пользователя" в UI для не-администраторов, но сам API-эндпоинт DELETE /api/users/{id} не проверяет роль вызывающего. Атакующий, знающий структуру API (или угадавший её), напрямую делает запрос — и операция выполняется [12].
Два громких реальных инцидента иллюстрируют масштаб:
- Texas Department of Insurance (2022): BFLA-уязвимость три года оставалась незамеченной. Были раскрыты персональные данные почти двух миллионов жителей штата из страховых претензий [11].
- Optus (2022): через BFLA злоумышленник получил доступ к почти 10 миллионам записей клиентов второго по величине телекоммуникационного оператора Австралии [11].
BFLA часто возникает по трём причинам:
- Авторизация реализована только на уровне UI, а не на уровне API-эндпоинта.
- Сложные иерархии ролей не покрываются тестами на уровне каждой функции.
- "Shadow API" и "Zombie API" — устаревшие или незарегистрированные эндпоинты, о которых команда безопасности не знает, но они продолжают работать в продакшене [13].
Принципиальное различие между BOLA и BFLA: BOLA — атакующий обращается к разрешённой функции, но с чужим объектом. BFLA — атакующий вызывает функцию, к которой у него нет доступа вообще [12].
JWT: "я подделываю токен и получаю чужую роль"
JSON Web Token (JWT, RFC 7519) стал де-факто стандартом для аутентификации и авторизации в API. Токен содержит payload с данными пользователя (ID, роль, tenant), подписан сервером и передаётся в каждом запросе. Но "подписан" не означает "неуязвим" [14].
Атаки на JWT делятся на несколько классов:
Algorithm confusion (alg: none). Спецификация JWT допускает значение alg: none — "неподписанный токен". Уязвимые библиотеки принимают такой токен как валидный. Атакующий меняет алгоритм в заголовке на none, удаляет подпись и произвольно редактирует payload, включая роль (role: admin) [15]. Защита: использовать whitelist разрешённых алгоритмов, никогда не брать значение alg из самого токена.
RS256 → HS256 confusion. Если сервер ожидает RS256 (асимметричный), но принимает HS256 (симметричный), атакующий может подписать токен публичным ключом сервера как HMAC-секретом. В 2015 году эта уязвимость была массово обнаружена в популярных JWT-библиотеках [16].
Weak secret. Если HMAC-секрет короткий или словарный — он брутфорсится оффлайн. Подобранный секрет позволяет подписывать произвольные токены.
Token leakage. Access tokens — bearer-токены: кто предъявил, тот и авторизован. Утечка токена в URL (например, при использовании Implicit Grant в OAuth 2.0), в браузерной истории, в логах или localStorage даёт атакующему полный доступ до истечения срока жизни [17].
Важно: авторизационная проверка на уровне JWT — только первый рубеж. JWT подтверждает, что пользователь тот, за кого себя выдаёт. Но он не гарантирует, что этот пользователь имеет право на конкретный объект или функцию. JWT и ownership-проверки — два разных слоя защиты.
Часть 2. OAuth 2.0: когда стандарт правильный, а реализация нет
OAuth 2.0 — протокол делегированной авторизации, не аутентификации. Его задача: дать клиентскому приложению ограниченный доступ к API от имени пользователя, без передачи пароля. Но большинство инцидентов происходят не из-за дефектов стандарта, а из-за типичных ошибок реализации [18].
По данным Okta State of Secure Identity Report (2024), 41% инцидентов, связанных с компрометацией учётных данных, были вызваны избыточными правами пользователей [19]. По данным Auth0 (2024), 74% security-инцидентов в SaaS — следствие чрезмерных разрешений, а не эксплуатации уязвимостей в коде [19].
Типичные ошибки в OAuth 2.0
Implicit Grant — устаревший и опасный flow. Access token возвращается напрямую в URL-фрагменте, что делает его видимым в браузерной истории, referrer-заголовках и серверных логах. OAuth 2.1 полностью исключил Implicit Grant [20].
Отсутствие PKCE. Proof Key for Code Exchange (RFC 7636) защищает Authorization Code Flow от перехвата authorization code. Без PKCE атакующий, перехвативший code, может обменять его на токен. OAuth 2.1 делает PKCE обязательным для всех клиентов [20].
Слабая валидация redirect URI. Если сервер принимает не точное совпадение, а частичное — атакующий регистрирует redirect_uri=https://evil.com?legit.example.com и перехватывает code. Redirect URI должен быть строгим allowlist с точным совпадением [21].
Отсутствие state-параметра. State защищает от CSRF-атак в OAuth flow. Пропуск параметра или его неправильная валидация позволяют атакующему инициировать авторизацию за пользователя [21].
Confusion: OAuth ≠ аутентификация. OAuth выдаёт access token, но не утверждает личность пользователя. Использовать OAuth вместо OpenID Connect (OIDC) для проверки identity — распространённая архитектурная ошибка [18].
Чрезмерно широкие скоупы. Запрашивать scope admin:all вместо read:orders — нарушение принципа наименьших привилегий. Компрометация токена с широким scope даёт атакующему максимальный доступ [19].
Хранение токенов в localStorage. LocalStorage уязвим к XSS. По данным OWASP, неправильное хранение токенов составляет более 30% случаев их компрометации в веб-приложениях [22].
RFC 9700 — Security Best Current Practice для OAuth 2.0, опубликованный в январе 2025 года — фиксирует все эти проблемы и является обязательным чтением для команд, строящих OAuth-авторизацию [23].
Часть 3. Модели авторизации: RBAC, ABAC и когда что применять
Выбор модели авторизации определяет сложность реализации, удобство поддержки и гибкость.
RBAC — Role-Based Access Control
Классическая модель: пользователь получает роль, роль получает права. Middleware проверяет роль пользователя (чаще всего из JWT) и сверяет её со списком разрешённых ролей для данного маршрута [24].
RBAC прост в реализации и понятен большинству команд. Однако у него есть принципиальное ограничение: он не проверяет принадлежность объекта. Роль manager может разрешать GET /api/orders/{id}, но не отвечает на вопрос "принадлежит ли этот заказ именно этому менеджеру?". BOLA остаётся открытой.
Второй недостаток — role sprawl: по мере роста продукта число ролей взрывообразно растёт. Роль developer-prod-readonly, developer-staging-write, ops-ec2-readonly — каждая требует поддержки и аудита.
ABAC — Attribute-Based Access Control
ABAC принимает решение на основе атрибутов пользователя, ресурса и контекста запроса. Правило может быть таким: "пользователь с department=finance в рабочие часы имеет доступ к ресурсам с classification=financial в своём регионе". Центральная policy-engine вычисляет решение [25].
ABAC решает проблему role sprawl, но усложняет реализацию и порождает новую: reverse lookup ("у кого есть доступ к этому ресурсу?") требует перебора атрибутов всех пользователей [26].
В мире микросервисов ABAC часто реализуется через Open Policy Agent (OPA) или AWS Verified Permissions, с возможностью смешивать RBAC- и ABAC-логику в одной policy [26].
ReBAC — Relationship-Based Access Control
ReBAC определяет права через граф отношений: owner-of, member-of, manager-of. Лучший пример — Google Drive: файл наследует права папки. OpenFGA (используется в Okta), SpiceDB от Authzed реализуют этот подход [26].
ReBAC хорошо подходит для иерархических структур данных и сценариев совместной работы, но требует дополнительных инвестиций в инфраструктуру policy-engine.
Практический вывод по выбору модели
Для большинства продуктов рациональная стратегия — гибридный подход: RBAC для грубого контроля доступа на уровне функций (какие эндпоинты доступны какой роли), плюс ownership-проверки на уровне объектов (принадлежит ли ресурс текущему пользователю). По мере усложнения — ABAC для контекстуально-зависимых сценариев (временны́е окна, гео, классификация данных).
Какую бы модель вы ни выбрали: авторизация должна работать на стороне сервера. Проверки только на уровне UI — иллюзия безопасности, которую разбивает первый же прямой запрос к API [2].
Часть 4. Секреты в коде: самая предотвратимая утечка
В 2024 году GitHub зафиксировал более 39 миллионов утёкших секретов — API-ключей, паролей, токенов — в публичных репозиториях [4]. По данным IBM, средняя стоимость инцидента, связанного с компрометацией учётных данных, составила 4,88 млн долларов в 2024 году — рост на 10% год к году [5].
Это наиболее распространённая и наиболее предотвратимая причина нарушения авторизации. Механика понятна: разработчик хардкодит API-ключ или токен в исходный код, делает коммит, репозиторий (или его форк) становится публичным — и секрет индексируется. Даже если ошибку замечают и коммит удаляют — git-история сохраняет артефакт. Большинство утёкших секретов никогда не ротируются: по данным GitGuardian, 70% секретов, утёкших в 2022 году, оставались активными в 2025 году [5].
Взлом US Treasury в 2024 году был прослежен именно к утёкшему API-ключу для BeyondTrust — одному скомпрометированному секрету хватило, чтобы обойти многомиллионные инвестиции в безопасность [5].
Что защищает от этого:
- Push Protection в GitHub и GitLab — блокирует пуш, если в диффе обнаружены известные паттерны секретов.
- Secret Manager / Vault — хранение секретов вне кода: AWS Secrets Manager, HashiCorp Vault, Azure Key Vault.
- Переменные окружения и CI/CD secrets — не в коде, не в конфигах, не в .env-файлах, попавших в репозиторий.
- Ротация секретов — компрометация статического долгоживущего ключа значительно опаснее, чем кратковременного токена с автоматической ротацией.
- Scoping — секрет должен иметь минимально необходимые права. Утечка read-only ключа для одного сервиса менее разрушительна, чем утечка master admin key.
Часть 5. Multi-tenant SaaS: изоляция данных как фундамент авторизации
В multi-tenant системах авторизация обретает дополнительное измерение: нужно не просто убедиться, что пользователь видит свои данные, — нужно гарантировать, что данные одного тенанта физически или логически недоступны другому.
По данным Verizon DBIR 2024, человеческий фактор (ошибки, злоупотребления, компрометация учётных данных) присутствует в большинстве утечек. В multi-tenant системе скомпрометированный аккаунт с доступом к данным одного тенанта потенциально открывает доступ ко всем, если изоляция реализована неправильно [27].
Три основных архитектурных модели изоляции данных [27]:
- Shared database, shared schema — все тенанты в одних таблицах, разделённых
tenant_id. Дёшево и масштабируемо, но критически зависит от наличияWHERE tenant_id = :current_tenantв каждом запросе. Одна пропущенная фильтрация — утечка данных соседнего тенанта. - Shared database, separate schema — у каждого тенанта своя схема. Снижает риск случайного cross-tenant запроса, но требует корректного роутинга и авторизации.
- Separate database per tenant — максимальная физическая изоляция. Высокая операционная стоимость.
Ключевые принципы безопасной multi-tenant авторизации:
tenant_idдолжен быть first-class security attribute, а не просто полем в базе данных. Он должен приходить из верифицированного JWT-клейма, а не из пользовательского ввода.- Каждое авторизационное решение должно валидировать соответствие tenant_id в сессии и в запрашиваемом объекте — не только при логине, но при каждом запросе [27].
- Row-Level Security (RLS) на уровне базы данных снижает зависимость от "кто-то помнит добавить WHERE" в прикладном коде.
- Tenant context должен передаваться по всей цепочке микросервисов, а не теряться при межсервисных вызовах.
Часть 6. Авторизация в CI/CD и конфигурационных файлах
Авторизационные уязвимости не ограничиваются прикладным кодом. CORS-мисконфигурация, debug-флаги, открытые administrative endpoints в тестовых окружениях, излишне широкие IAM-роли в облаке — всё это входит в поверхность атаки.
На Хабре зафиксирован паттерн: zombie API и shadow API — устаревшие или незарегистрированные эндпоинты — становятся "воротами" для атакующих. В 2025 году была раскрыта веб-скимминг кампания, где атакующие использовали устаревший эндпоинт Stripe для проверки украденных карт [13].
В контексте CI/CD:
- Секреты в
.github/workflows, Dockerfile, terraform-конфигах — одна из наиболее частых форм утечки. - IAM-роли CI/CD-агентов часто получают избыточные права "чтобы всё работало" и не ревьюируются так тщательно, как прикладной код.
- Pull request от внешнего контрибьютора с изменением workflow-файла может получить доступ к секретам репозитория через GitHub Actions environment.
Именно здесь разрыв между "код работает" и "код безопасен" особенно заметен. AI-ассистированный код ускоряет разработку, но паттерны авторизации — широкий CORS (Access-Control-Allow-Origin: *), fallback-ключи, debug-режим, открытые admin-эндпоинты без проверки роли — регулярно попадают в PR незамеченными.
Команды, использующие AI для разработки, сталкиваются с ситуацией: PR функционально корректен и проходит тесты, но содержит авторизационное допущение, которое откроет чужие данные в продакшене. Дифы дочитывают не до конца, а новая зависимость с уязвимостью в access control приезжает незаметно. Инструменты, которые анализируют конкретное изменение — не весь репозиторий, а именно диф — и объясняют риск понятным языком до merge, помогают закрыть этот разрыв. HighVibe работает именно как такой слой контроля: анализирует PR с точки зрения безопасности и даёт команде короткий ответ — можно ли безопасно принять это изменение сейчас.
Часть 7. Практический чек-лист: что проверять на каждом уровне
До написания кода (проектирование)
- Для каждого API-эндпоинта определён: кто имеет доступ (роль), к каким объектам (ownership), при каких условиях (контекст).
- Выбрана модель авторизации (RBAC / ABAC / гибрид) и задокументирована.
- Определены tenant-boundaries и модель изоляции данных для multi-tenant систем.
- Принцип наименьших привилегий заложен в дизайн скоупов OAuth и IAM-политик.
В коде (реализация)
- Авторизационные проверки реализованы на стороне сервера, а не только в UI.
- Каждый эндпоинт проверяет: аутентификацию (валидный токен) + авторизацию (роль) + ownership (принадлежит ли объект этому пользователю).
- JWT: используется whitelist алгоритмов;
alg-значение из токена не используется для выбора алгоритма верификации; установлен короткий TTL; refresh token rotation включён. - Нет секретов в коде, конфигах или переменных окружения в репозитории.
- CORS настроен на конкретные разрешённые домены, не
*. - Все administrative endpoints требуют явной проверки роли, не полагаются на "скрыто в UI".
В PR (перед merge)
- Изменения в авторизационной логике, middleware, конфигурационных файлах прошли security review.
- Новые зависимости проверены на известные уязвимости (advisories, SCA).
- Изменения в CI/CD-файлах и IAM-политиках ревьюируются с повышенным вниманием.
- Нет debug-режима, открытых admin-эндпоинтов или тестовых bypass-флагов.
В продакшене (мониторинг)
- Логируются все отказы авторизации с достаточным контекстом (user ID, resource ID, endpoint).
- Настроены алерты на аномальный перебор ID (признак IDOR-атаки).
- Ведётся инвентаризация всех API-эндпоинтов, включая legacy и internal.
- Регулярная ротация секретов и аудит активных API-ключей и токенов.
- Push Protection для секретов включён на уровне организации.
Тестирование
- В тест-план входят авторизационные сценарии: попытка доступа к объекту другого пользователя; попытка вызова административной функции с пользовательской ролью; манипуляция ID в URL, теле запроса, заголовках.
- DAST-инструменты настроены на параметрическое тестирование объектных идентификаторов.
- Проводится ревизия "shadow API" и устаревших эндпоинтов в продакшене.
Ошибки и антипаттерны, которые открывают чужие данные
- "Авторизован = имеет право на всё" — логическая ошибка, порождающая BOLA.
- "Скрыто в UI = недоступно" — UI-контроль без серверной проверки — иллюзия защиты.
- "Это внутренний эндпоинт" — network boundary не заменяет авторизацию.
- "JWT подписан — значит можно доверять всем claim-ам" — подделка claim-ов через
alg: noneили слабый секрет. - "Мы тестируем happy path" — отсутствие негативных авторизационных тест-кейсов.
- "Секрет в .env-файле, это не в коде" — .env нередко попадает в репозиторий, в Docker-образ или в переменные окружения CI/CD, которые логируются.
- "Один токен для всего" — широкоскопный долгоживущий токен при компрометации открывает всё.
Тренды 2025–2026: что меняется
OAuth 2.1 (черновик IETF draft-ietf-oauth-v2-1-15, технически стабильный) закрывает несколько классов уязвимостей: удаляет Implicit Grant, делает PKCE обязательным, требует точного совпадения redirect URI. Реализации ведущих authorization server-ов уже поддерживают эти требования [20].
Sender-constrained tokens (mTLS, DPoP) делают bearer token бесполезным при краже: токен привязан к конкретному клиенту и не может быть использован с другого устройства или IP. Это следующий шаг после ротации токенов [22].
AI-ассистированный код создаёт новый вектор: авторизационные паттерны в сгенерированном коде не всегда соответствуют требованиям конкретного приложения. Число PR растёт, глубина ревью падает. Автоматический анализ безопасности на уровне PR становится не nice-to-have, а операционной необходимостью [13].
Регуляторная нагрузка в России усиливается: штрафы по №420-ФЗ до 500 млн рублей за утечку персональных данных делают авторизационные уязвимости напрямую финансово значимыми для бизнеса [7].
Заключение: что делать прямо сейчас
Авторизация — не задача "один раз настроить". Это инфраструктурный слой, который нужно проектировать, тестировать и мониторить на протяжении всей жизни продукта.
Первые три шага, которые дают максимальный эффект:
- Аудит эндпоинтов на BOLA: взять любой эндпоинт, который принимает ID объекта, и проверить, есть ли ownership-проверка на стороне сервера. Если нет — это IDOR в продакшене.
- Инвентаризация API: найти shadow API и zombie API. Эндпоинт, о котором никто не знает, — это атакующий, который нашёл его первым.
- Аудит секретов: запустить secret scan по репозиториям, включить Push Protection, проверить, какие секреты ни разу не ротировались.
Если команда использует AI для разработки — добавьте security-проверку на уровне PR как обязательный шаг, а не опциональный. "Работает" и "безопасно" — разные утверждения, и разрыв между ними часто находится именно в авторизационной логике.
Источники
[1] OWASP — "A01:2025 Broken Access Control" — https://owasp.org/Top10/2025/A01_2025-Broken_Access_Control/
[2] IntelligenceX Cybersecurity Blog — "Broken Access Control (A01:2025): The #1 OWASP TOP 10 Vulnerability" — https://blog.intelligencex.org/broken-access-control-owasp-a01-2025-complete-guide
[3] CNews — "Атак через API стало на 630% больше" — https://safe.cnews.ru/news/line/2025-01-31_atak_cherez_api_stalo_na_630
[4] GitHub Blog — "GitHub found 39M secret leaks in 2024. Here's what we're doing to help" — https://github.blog/security/application-security/next-evolution-github-advanced-security/
[5] ZeroHost Security Blog — "23.8 Million Secrets Leaked on GitHub: The Case for Credentials That Expire" — https://zerohost.net/blog/github-secrets-leaked-case-for-expiring-credentials/
[6] DevSec Blog — "Web API Security Champion Part II: Broken Authentication (OWASP TOP 10)" — https://devsec-blog.com/2024/05/web-api-security-champion-part-ii-broken-authentication-owasp-top-10/
[7] itsec.ru — "Защита API от бот-атак и эксплуатации уязвимостей" — https://www.itsec.ru/articles/zashchita-api-ot-bot-atak-i-ekspluatacii-uyazvimostej
[8] OWASP — "API1:2023 Broken Object Level Authorization" — https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/
[9] SentinelOne — "What Is Insecure Direct Object Reference (IDOR)?" — https://www.sentinelone.com/cybersecurity-101/cybersecurity/insecure-direct-object-reference/
[10] AuthenticOne — "IDOR Vulnerabilities: The Hidden Pathway to Data Breaches" — https://authenticone.com/idor-the-silent-gateway-to-data-breaches/
[11] Barracuda Networks Blog — "OWASP Top 10 API security risks: Broken function level authorization" — https://blog.barracuda.com/2023/06/19/owasp-top-10-api-broken-funtion-level-authorization
[12] ApyGuard — "OWASP API Security Top 10 (2023): Every Vulnerability Explained With Fixes" — https://www.apyguard.com/resources/blog/owasp-api-security-top-10
[13] Habr — "API-безопасность 2026: почему защита требует нового подхода" — https://habr.com/ru/amp/publications/1025962/
[14] Authgear — "JWT Security Best Practices: Common Vulnerabilities" — https://www.authgear.com/post/jwt-security-best-practices-common-vulnerabilities/
[15] Vaadata — "JWT: Vulnerabilities, Attacks & Security Best Practices" — https://www.vaadata.com/en/blog/jwt-json-web-token-vulnerabilities-common-attacks-and-security-best-practices/
[16] 42Crunch — "7 Ways to Avoid API Security Pitfalls when using JWT or JSON" — https://42crunch.com/7-ways-to-avoid-jwt-pitfalls/
[17] Curity — "JWT Security Best Practices: Checklist for APIs" — https://curity.io/resources/learn/jwt-best-practices/
[18] Treblle — "OAuth 2.0 for APIs: Flows, Tokens, and Pitfalls" — https://treblle.com/blog/oauth-2.0-for-apis
[19] Java Code Geeks — "OAuth 2.1 and Modern Authentication Patterns: What's Deprecated and What's Recommended" — https://www.javacodegeeks.com/2025/12/oauth-2-1-and-modern-authentication-patterns-whats-deprecated-and-whats-recommended.html
[20] Aembit — "OAuth 2.0 vs 2.1: What Changed and How to Migrate" — https://aembit.io/blog/oauth-2-1-guide-migration-security/
[21] Duende Software — "7 Common OAuth 2.0 Implementation Security Pitfalls" — https://duendesoftware.com/learn/7-common-security-pitfalls-oauth-2-0-implementations
[22] InboxAgents — "OAuth Security Best Practices for 2025" — https://inboxagents.ai/blog/oauth-security-best-practices
[23] IETF — "RFC 9700: Best Current Practice for OAuth 2.0 Security" — https://www.rfc-editor.org/rfc/rfc9700.html
[24] DreamFactory Blog — "RBAC vs ABAC: API Security Implications" — https://blog.dreamfactory.com/rbac-vs-abac-api-security-implications
[25] NIST — "Attribute-Based Access Control for Microservices-based Applications (SP 800-204B)" — https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-204B.pdf
[26] DEV Community — "RBAC vs ABAC vs ReBAC: How to Choose and Implement Access Control Models" — https://dev.to/kanywst/rbac-vs-abac-vs-rebac-how-to-choose-and-implement-access-control-models-3i2d
[27] Redis — "Data isolation in multi-tenant SaaS" — https://redis.io/blog/data-isolation-multi-tenant-saas/