Экзамен SC весны 2024, задание 1 части PM: JWT alg=none, авторизация API и временные меры WAF

· Обновлено: · · специалист по обеспечению информационной безопасности, экзамен SC, API, безопасность API, JWT, аутентификация, авторизация, WAF, Log4Shell, информационная безопасность, уязвимость, IPA

История изменений (1 обновлений, последнее 31 Aug 2026)

Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.

Русский текст переписан по текущему навыку технического перевода как полный перевод японского оригинала.
Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.22176013)

Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.

Го Комура (2026). Экзамен SC весны 2024, задание 1 части PM: JWT alg=none, авторизация API и временные меры WAF. KomuraSoft LLC. https://comcomponent.com/ru/blog/sc-exam-r6s-pm-q1-api-security/

DOI (зарегистрированный архив)
10.5281/zenodo.22176013
DOI (последняя зарегистрированная версия)
10.5281/zenodo.22176014

«Подпись JWT проверяем, значит идентификатору пользователя можно доверять.»

Это верно лишь наполовину.

Задание 1 части PM весеннего экзамена IPA 2024 года на квалификацию «специалист по обеспечению информационной безопасности» (情報処理安全確保支援士, SC) построено вокруг API, которое вызывает приложение на смартфоне.1 После успешной аутентификации выдаётся JWT, и этот JWT прикладывают к вызовам API, которое читает и обновляет сведения о пользователе. На первый взгляд — обычная схема.

Но диагностика находит четыре проблемы.

  1. Если сменить alg в заголовке JWT на none, проходит неподписанный JWT.
  2. Оставив действительный JWT и сменив mid на чужой идентификатор, можно прочитать или обновить чужие сведения.
  3. Если добавить не описанный в спецификации status=paid, бесплатный пользователь становится платным.
  4. Четырёхзначный код из письма можно перебирать без ограничения числа попыток.

Все четыре выглядят как «уязвимости вокруг аутентификации», но причины разные. Ломаются отдельные границы: целостность токена, авторизация на уровне объекта, авторизация на уровне свойства и ограничение числа попыток.

Во второй половине задания появляется ещё одна тема. В широко используемой открытой библиотеке публикуют критическую уязвимость: через JNDI Lookup можно выполнить код извне. Ни исправленной версии, ни готового правила WAF ещё нет. Пока что спрашивают, как подтвердить воздействие, что должна смотреть WAF и почему сначала ставят режим «обнаружение», а не «блокировка».

Статья опирается на официальные образцовые ответы2 и комментарий к оцениванию3. Разбираем не только ответ на каждый вопрос, но и почему это ответ и насколько строже это стоит проектировать на практике.

Общая картина заданияПоказывает, какая граница доверия ломается на каждом этапе - код аутентификации, JWT, авторизация API и уязвимость библиотекиНет лимита попытокДопускает alg=noneДоверяет midstatus=paidJNDI/LDAP/HTTPПриложение пользователяЧетырёхзначный кодВыдача JWTAPI пользователяЗапись в журналУязвимая библиотекаВыполнение внешнего кодаЧтение/обновление чужих данныхСмена платёжного статуса

Рис. 1: Общая картина задания. На каждом этапе ломается другая граница доверия.

1. Сначала выводы

  • Свойство RESTful API не вести сессию называют stateless (без сохранения состояния). Это не значит, что у сервера нет базы данных и нет пользовательского состояния
  • У четырёхзначного кода 10 000 вариантов. При 10 попытках в секунду в среднем хватает 5 000 попыток, то есть 500 секунд. Это короче срока действия в 10 минут, поэтому одного истечения мало
  • Минимальная мера против alg=none — убедиться, что alg в заголовке JWT не равен NONE. На практике разрешённые алгоритмы фиксируют на стороне сервера
  • Даже при действительном JWT mid из запроса доверять нельзя. Либо сверяйте идентификатор из JWT с mid, либо — безопаснее — вовсе не принимайте mid и определяйте объект из JWT
  • Добавление status=paid — это Mass Assignment: свойства вне спецификации сразу связываются с внутренним объектом. DTO обновления используйте как список разрешённых полей и не давайте пользователю менять платёжный статус
  • Образцовый ответ против перебора — обработка, которая блокирует учётную запись, когда число последовательных неудач превышает порог. На практике поверх этого ставят ступенчатые задержки и контроль по источнику
  • Чтобы подтвердить воздействие только что опубликованной критической уязвимости, вместо разрушающей команды записывают обращения к index.html тестового сервера и так проверяют, что выполнение внешнего кода действительно доходит
  • Строка атаки приходит в HTTP-заголовке, поэтому объект проверки WAF — Header. Регулярное выражение, которое учитывает смену регистра, например \W[jJ][nN][dD][iI]\W
  • Польза сначала поставить WAF в режим «обнаружение» в том, что можно не допустить блокировку рабочего трафика из-за ложного срабатывания. Получив оповещение, разбирают, атака это или нет, правят правило и затем переходят к блокировке
  • WAF — временная мера. Устранение причины — обновить затронутую библиотеку до исправленной версии

На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 18, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle

2. Как сюжет ложится на вопросы

Действие происходит в компании G, которая запускает новый сервис для здоровья. Пользователи через приложение на смартфоне вводят приёмы пищи, вес и подобное и получают оценку риска для здоровья и советы по меню. Система собрана в облаке: API-шлюз, событийная обработка и управляемая база данных.

Имена продуктов и сервисов в билете абстрагированы. Эта статья тоже не воспроизводит схемы и текст IPA, а пересказывает только структуру, без которой вопросы не понять.

Вопрос Тема Глава статьи
Вопрос 1 Свойства RESTful API Глава 4
Вопрос 2(1) Время перебора 4-значного кода Глава 5
Вопрос 2(2) JWT alg=none Глава 6
Вопрос 2(3) Доступ к другому пользователю через mid Глава 7
Вопрос 2(4) Приём status вне спецификации Глава 8
Вопрос 2(5) Меры против перебора Глава 9
Вопрос 3(1) Безопасно подтвердить, что уязвимость есть Глава 11
Вопрос 3(2)(3) Что смотрит WAF и регулярное выражение Глава 12
Вопрос 3(4) Польза режима обнаружения и как им пользоваться Глава 13

Комментарий к оцениванию отмечает, что общая доля верных ответов была примерно средней. Ниже она была у меры против подделки JWT в вопросе 2(2) и у механизма, который нужен на проверочном сервере в вопросе 3(1). Ни то ни другое не решается словарём. Нужно проследить, какое значение сменил злоумышленник, в какую обработку оно попало и где ему в итоге доверились.

3. Это не одна «проблема аутентификации»

Если разложить всё задание по границам доверия, получается так.

[Идентификатор и пароль]
          |
          v
[Проверка 4-значного кода] ---- нет лимита попыток ----> перебор
          |
          v
[Выдать JWT]
          |
          v
[Библиотека JWT] ------- допускает alg=none ------> подделка идентификатора
          |
          v
[API пользователя]
    |             |
    |             +-- отдаёт status целиком ----> нет авторизации на уровне свойства
    |
    +-- доверяет mid -----------------------> нет авторизации на уровне объекта

[Записать внешний ввод в журнал]
          |
          v
[Уязвимая библиотека] ---- JNDI/LDAP/HTTP ------> выполнение внешнего кода

Самое важное различение здесь такое.

Проверка Что спрашивает Что сломано в этом задании
Аутентификация Кто вы Перебор 4-значного кода
Проверка токена Не подделаны ли эти сведения о личности alg=none
Авторизация на уровне объекта Можно ли этому пользователю обращаться к этим данным Подмена mid
Авторизация на уровне свойства Можно ли менять это поле status=paid
Граница от ввода к исполнению Не исполняется ли внешний ввод как команда JNDI Lookup

Успех предыдущей проверки — не причина пропускать следующую. Пользователь с действительным JWT не обязательно вправе читать чужие данные. Пользователь, которому можно обновлять свои данные, не обязательно вправе менять ещё и платёжный статус.

Когда эти стадии разделены, ответ на каждый вопрос перестаёт быть тем, что заучивают.

Разница между аутентификацией и авторизациейАутентификация подтверждает субъекта, авторизация подтверждает, какие операции этому субъекту разрешеныАутентификациякто выАвторизациячто вам можно

Рис. 2: Разница между аутентификацией и авторизацией. Сначала аутентификация; авторизация — отдельная проверка.

4. Вопрос 1 — что такое stateless

Вопрос 1 спрашивает об одном из принципов проектирования RESTful API: свойстве не вести управление сессией.

Ответ — stateless (без сохранения состояния).

Stateless значит: серверу не нужно помнить состояние предыдущего запроса, потому что каждый запрос сам несёт всё, что нужно для обработки. В этом задании приложение на смартфоне в каждом запросе кладёт JWT в заголовок Authorization. Сервер проверяет JWT и по нему определяет пользователя этого запроса.

Частая ошибка — читать «без состояния» как «сервер вообще ничего не хранит». На деле он обычно хранит следующее.

  • Базу данных со сведениями о пользователях и данными о здоровье
  • Платёжный статус
  • Значение кода аутентификации, срок действия и счётчик неудач
  • Ключ подписи JWT
  • Сведения об отзыве, если в проекте есть список отозванных токенов
  • Журналы и аудиторские записи

Чего он не держит — это серверное состояние сессии, которое существует только чтобы продолжать диалог и без которого каждый вызов API якобы не обойтись.

Stateless само по себе безопасность не повышает. JWT в каждом запросе упрощает горизонтальное масштабирование, но ошибка в проверке JWT так же равномерно расходится по всем узлам. Архитектурное свойство и корректность защиты — разные вещи.

5. Вопрос 2(1) — четырёхзначный код в среднем подбирают за 500 секунд

API аутентификации, если идентификатор и пароль совпали, отправляет на почту четырёхзначное число. Затем выдаёт JWT, когда совпали идентификатор и этот код. Код действителен 10 минут с момента генерации.

При диагностике было возможно 10 попыток в секунду. Спрашивают, за сколько секунд в среднем удаётся прорваться.

Счёт — «половина числа вариантов»

У четырёхзначного числа, включая ведущий ноль, следующие 10 000 вариантов.

0000, 0001, 0002, ... , 9999

Если верный код выбран равномерно, злоумышленник, который перебирает варианты по порядку без повторов, в среднем доходит до верного после половины пространства.

Среднее число попыток = 10 000 / 2 = 5 000
Среднее время          = 5 000 / 10 попыток в секунду = 500 секунд

Значит, в поле b ответ — 500.

В худшем случае уходит до 1 000 секунд, но вопрос спрашивает среднее. Срок действия кода — 600 секунд, то есть длиннее среднего времени подбора в 500 секунд. Поэтому и решили, что «вероятность прорыва высокая».

Оценка времени для четырёхзначного кодаПеребор 10 000 вариантов по 10 в секунду в среднем даёт 5 000 попыток и 500 секунд, что меньше срока действия 600 секундВ среднем 10 000 / 2 = 5 000 попыток500 секунд меньше 600 секунд10 000 вариантовСреднее время подбора 500 секундСрок действия 600 секундМожно подобрать в срок действия

Рис. 9: Оценка времени для четырёхзначного кода. Перебор в среднем половины вариантов укладывается в срок действия.

Одного укороченного срока истечения мало, если вариантов немного

Сила кода аутентификации не определяется ни одной разрядностью, ни одним сроком действия.

Число попыток за срок действия
= попытки в секунду × срок действия
= 10 × 600
= 6 000 попыток

Перебирая неповторяющиеся значения по порядку, за срок действия можно проверить 60% из 10 000 вариантов. Задать время истечения само по себе недостаточно, если число попыток не ограничено.

Действующий NIST SP 800-63B требует не менее шести цифр для короткого секрета во внеканальной (out-of-band) аутентификации и предписывает лимит попыток, если у секрета меньше 64 бит энтропии. Он также требует не использовать почту для внеканальной аутентификации.4 На экзамене отвечают внутри данной спецификации — четырёхзначный код, отправка по почте. В новом проекте на практике стоит пересмотреть и саму эту предпосылку.

6. Вопрос 2(2) — alg=none это «дать злоумышленнику выбрать метод проверки»

JWT в этом задании состоит из трёх частей: заголовок, payload и подпись.

base64url(header).base64url(payload).base64url(signature)

В заголовке как алгоритм подписи был записан RS256. В payload — идентификатор пользователя, время выдачи и срок действия.

Специалист, проводивший диагностику, изменил следующее.

  1. Сменил alg заголовка с RS256 на NONE.
  2. Сменил идентификатор пользователя в payload на другого пользователя.

Этот JWT приняли: проверка прошла, и удалось выдать себя за другого.

Ход атаки JWT alg=noneВ действительном JWT меняют alg на none, переписывают идентификатор - и запрос проходитСменить alg заголовка на noneПропускает проверку подписиДействительный JWTalg=RS256user=user01Подделанный JWTalg=noneuser=user02Сервер принимает егокак user02

Рис. 3: Ход атаки JWT alg=none. Алгоритм проверки выбирает злоумышленник.

none — не опечатка

RFC 7519 определяет «Unsecured JWT» — JWT без подписи и без шифрования, у которого alg равен none.5 То есть значение none в спецификации есть.

Проблема в том, что API, которое должно принимать только подписанные JWT, приняло указанный злоумышленником none.

Уязвимая обработка концептуально выглядит так.

1. Прочитать заголовок JWT.
2. Посмотреть alg, записанный в заголовке, и выбрать метод проверки.
3. Если alg равен none, подпись не проверять.
4. Доверить идентификатору пользователя в payload.

Силу защиты выбирают из ввода, который контролирует злоумышленник.

Ответ экзамена

Вопрос спрашивает, не более чем в 20 знаках каждое, какие данные должна проверять исправленная библиотека Q и что эта проверка должна подтвердить.

Образцовый ответ таков.

Пункт Суть ответа
Данные для проверки Значение, указанное в alg заголовка JWT
Что проверить Что оно не равно NONE

Как прямое исправление уязвимости из текста задания это верно.

На практике не останавливаться на «всё, кроме NONE»

Здесь нужно отделить ответ экзамена от практической рекомендации.

RFC 8725 говорит, что библиотека JWT должна дать вызывающему указать множество разрешённых алгоритмов и ничего вне этого множества не использовать.6 Иными словами, идея такая.

Плохой подход:
  Принять, если token.header.alg != "none"

Хороший подход:
  Принять только если входит в serverConfig.allowedAlgorithms
  например allowedAlgorithms = ["RS256"]

Отклонять только none всё равно может оставить другие слабые алгоритмы или путаницу, когда схему с открытым ключом принимают за симметричную. Принцип — не копить отрицательные условия «что не принимать», а узко зафиксировать положительное множество «что разрешено».

Проверка JWT должна подтверждать не только алгоритм, но, в зависимости от сценария, как минимум ещё следующее.

Пункт Что подтвердить
Подпись Проверяется ли ожидаемым ключом и алгоритмом
iss Это доверенный издатель
aud Токен выдан для этого API
exp Срок действия не истёк
nbf Не раньше времени «не действителен до»
sub или идентификатор Это действительный субъект в приложении
Тип токена Не путают ли ID-токен с токеном доступа и т. п.

В этом задании ключ payload называется user, но на практике лучше использовать стандартный sub либо явно определить смысл собственного клейма (claim).

Безопасная и небезопасная проверка JWTНебезопасная проверка зависит от alg, безопасная использует список разрешённых алгоритмов на сервереБезопасная проверкаРазрешённые алгоритмы в конфиге серверанапример RS256Подтвердить, что alg заголовка JWTв списке разрешённыхПроверить подпись, iss, aud, expНебезопасная проверкаПрочитать alg из заголовка JWTПринять, если alg равен none

Рис. 4: Безопасная и небезопасная проверка. На практике разрешённые алгоритмы фиксируют узко.

Base64url — не шифрование

Ещё одно частое недоразумение про JWT. Заголовок и payload записаны в base64url, но это не шифрование. Их может декодировать и прочитать кто угодно.

Подпись гарантирует — и только когда проверка проходит — что содержимое не меняли после выдачи. Это не разрешение класть в payload подписанного JWT личные сведения, которые нужно скрыть.

7. Вопрос 2(3) — даже с действительным JWT смена mid читала чужие данные

Дальше — атака, которая сам JWT не подделывает.

API пользователя принимает идентификатор под именем mid в GET или PUT. Общий модуль P по этому mid читает или обновляет сведения о пользователе в базе.

Устройство атаки простое.

Идентификатор в JWT: user01    <- корректно подписанный JWT
mid запроса:         user02   <- сменил злоумышленник

Подпись JWT действительна, поэтому аутентификация проходит. Но API доверяет mid=user02 как есть и возвращает сведения user02.

Это учебный случай того, что OWASP API Security Top 10 2023 называет Broken Object Level Authorization (BOLA). Всякий раз, когда к данным обращаются по идентификатору объекта, который указал пользователь, авторизацию именно для этого объекта нужно проверять каждый раз.7

Атака BOLAДействительный JWT при смене mid запроса на чужой идентификаторJWT user01mid user02Доверяет midЗлоумышленникAPI пользователяВозвращает данные user02 из БД

Рис. 5: Атака BOLA. Аутентификация проходит, авторизацию не проверяли.

Ответ вопроса

Подчёркивание ② в таблице 5 спрашивает, не более чем в 40 знаках, какую обработку добавить к вызову общего модуля P.

Образцовый ответ:

Обработка, которая проверяет, совпадает ли идентификатор пользователя в JWT со значением mid

Проверять внутри общего модуля P выгодно тем, что одну и ту же авторизацию легко применить и к GET, и к PUT, и к любому будущему API, которое тоже использует P. Копировать одно и то же сравнение в каждый экран или конечную точку значит, что где-то его не будет.

Более безопасный проект — вовсе не принимать mid

Для API, которое только читает или обновляет собственные сведения вызывающего, идентификатор пользователя от клиента принимать не нужно.

GET /users/me
Authorization: Bearer <JWT>

На стороне сервера субъект берут из уже проверенного JWT.

principal = validateJwt(request.authorization)
userId = principal.subject
return repository.getUser(userId)

То же для обновлений.

principal = validateJwt(request.authorization)
input = validateProfileUpdate(request.body)
repository.updateProfile(
    userId = principal.subject,
    name = input.name,
    age = input.age
)

Сравнение защищает, если его написать. Но проект, который вовсе не принимает целевой идентификатор снаружи, сокращает сам класс ошибок, в котором это сравнение просто забыли.

Если администратору нужно работать со сведениями другого пользователя, разделите так.

PUT /users/me                  для обычных пользователей
PUT /admin/users/{userId}      для администраторов

Для административного маршрута требуйте отдельные права, аудиторский журнал и при необходимости повторную аутентификацию. Границы политики авторизации так виднее, чем «добавить исключение в API обычного пользователя только для администраторов».

Как предотвратить BOLAВместо mid запроса решать или сверять цель по субъекту JWTGET /users/me + JWTВзять sub из JWTСовпадаетНе совпадаетНет midПользовательAPIЕсли mid есть,совпадает ли с subВернуть свои данныеОтклонить 403Поиск в БД по JWT sub

Рис. 6: Как предотвратить BOLA. Либо не принимать mid, либо сверять его с субъектом JWT.

Аутентификацию и авторизацию — в одном предложении

И на экзамене, и на практике помогает такая формулировка.

  • Аутентификация: кто вы
  • Авторизация: что этому человеку можно делать

Успешная проверка подписи JWT доводит вас только до «субъекту, которого представляет этот токен, можно доверять». Можно ли «этому субъекту читать user02» нужно подтверждать отдельно.

8. Вопрос 2(4) — status=paid это отсутствие авторизации на уровне свойства

Спецификация API пользователя определяет следующие параметры обновления.

mid   идентификатор пользователя
name  имя
age   возраст

Однако специалист, проводивший диагностику, добавил значение, которого нет в спецификации.

status=paid

После этого статус бесплатного пользователя сменился на статус платного.

Согласно заданию, сервис L не проверял полученные параметры, а передавал их все напрямую общему модулю P, и P был сделан так, чтобы уметь обновлять базу напрямую.

Ответ для поля c — общий модуль P.

Mass Assignmentstatus=paid вне спецификации добавляют и целиком переносят во внутренний объектЗлоумышленник добавляет status=paidАвтоматическое связываниеСохранено в БДСпецификация APImid / name / ageТело запросаОбщий модуль PПлатёжный статус сменён на paid

Рис. 7: Mass Assignment. Свойство вне спецификации целиком попадает во внутренний объект.

Отличие от BOLA

Подмена mid из предыдущей главы и это добавление status выглядят похоже, но уровень защиты разный.

Уязвимость Что меняет злоумышленник Что на самом деле нужно проверять
Подмена mid Целевой объект Можно ли этому пользователю обращаться к этой записи
Добавление status Свойство внутри объекта Можно ли этому пользователю менять это поле

OWASP API Security Top 10 2023 относит последнее к Broken Object Property Level Authorization и включает сюда то, что раньше называли Mass Assignment.8

«Класть JSON прямо в сущность» опасно

Уязвимая реализация концептуально выглядит так.

entity = repository.find(body.mid)
bindAllProperties(entity, body)
repository.save(entity)

Даже если на экране есть поля ввода только для name и age, злоумышленник может собрать HTTP-запрос напрямую. Отсутствие поля в интерфейсе — не граница безопасности.

Безопасная реализация делает обновляемые поля явными.

input = parseExactSchema(body, fields = ["name", "age"])

entity = repository.find(authenticatedUserId)
entity.name = input.name
entity.age  = input.age
repository.save(entity)

Здесь важны две вещи.

  1. В типе ввода для обновления должны быть только поля, которые пользователю разрешено менять.
  2. Неизвестные поля вне спецификации лучше не молча игнорировать, а по возможности отклонять как ошибку.

Молча игнорировать неизвестные поля скрывает, что атака не удалась, но также позволяет пропустить ошибки реализации клиента и признаки атаки. Если нет причины совместимости, отказ по строгой схеме упрощает расследование.

Авторизация на уровне свойстваDTO обновления держит только список разрешённых полей, неизвестные свойства отклоняютсяПроверка схемыДаНетОтдельный путьDTO обновления (список разрешённых полей)nameageТело запросаТолько разрешённыеполя присутствуютОбновить name/age сущностиВернуть ошибкуПлатёжный сервиспроверенное уведомлениеОбновить status=paid

Рис. 8: Авторизация на уровне свойства. Обновляемые поля ограничивают списком разрешённых, платёжный статус меняют другим путём.

status менять только из результата платежа

status=paid — не часть профиля пользователя. Это состояние, выведенное из серверного факта: платёж прошёл успешно.

Обновление профиля пользователя
  -> можно менять только name / age

Проверенное уведомление от платёжного сервиса
  -> сверить paymentId
  -> предотвратить повторную обработку
  -> сменить status на paid

Даже если это лежит в одном столбце базы, право менять и путь, которым меняют, — разные вещи. Использовать внутреннюю сущность напрямую как тип ввода внешнего API стирает эту границу.

9. Вопрос 2(5) — против перебора держат счётчик неудач как состояние

Против перебора четырёхзначного кода поле d в таблице 5 спрашивает, не более чем в 30 знаках, какую обработку туда поставить. Порог равен 10.

Образцовый ответ:

Обработка, которая блокирует учётную запись, когда число последовательных неудач превышает порог

Это не противоречит stateless из вопроса 1. Не держать состояние диалога вызова API как серверную сессию и сохранять счётчик неудач, нужный для решения о безопасности, — разные вещи.

С лимитом попыток и без негоБез лимита код подбирают в среднем за 500 секунд, лимит счётчика неудач резко замедляет атакуС лимитомСкорость атаки падаетУчётная запись заблокированаБлокировка после 10 неудачСтупенчатая задержкаБез лимитаОколо 500 секундАутентификация успешна10 попыток в секунду

Рис. 10: С лимитом попыток и без него. Лимит счётчика неудач практически останавливает перебор.

На практике не опираться только на постоянную блокировку

Лимит попыток по учётной записи нужен, но если злоумышленник знает чужой идентификатор, он может намеренно провалить 10 раз и запереть законного пользователя. Поэтому на практике сочетают следующее.

Контроль Роль
Счётчик неудач по учётной записи Останавливает перебор против одной учётной записи
Ступенчатые паузы Дают законному пользователю ошибиться при вводе и одновременно замедляют атаку
Контроль по IP источника, устройству, ASN и т. д. Сдерживает атаки, которые пробуют мало раз против многих учётных записей
Решения на основе риска Жёстче ограничивают необычные регионы, устройства или скорости
Уведомление пользователя Даёт заметить атаку или собственную ошибку
Безопасная процедура восстановления Не даёт каналу разблокировки самому стать путём атаки

Кроме того, при повторной отправке кода счётчик неудач нельзя сбрасывать в ноль — иначе злоумышленник пополняет бюджет попыток каждым вызовом API повторной отправки. Действующий NIST SP 800-63B также требует не сбрасывать счётчик неудач даже при генерации нового секрета аутентификации.4

Сделать код аутентификации одноразовым

В тексте задания в центре внимания срок истечения, но на практике нужно ещё следующее.

  • Сразу делать успешный код недействительным.
  • Отклонять повторное использование того же кода.
  • Не оставлять сам код в журналах.
  • Делать ответ таким, чтобы по успеху или неудаче проверки кода нельзя было вывести, существует ли пользователь.
  • Ставить лимит попыток и на API отправки кода.

Пока используется короткий секрет, безопасность нельзя оставлять одному случайному генерированию.

Меры для кода аутентификацииПомимо разрядности и срока защищают лимитом попыток, отказом от повторного использования, уведомлением и прочимКод аутентификацииУвеличить разрядностьСократить срок действияЛимит попытокСделать недействительным после успехаНе сбрасывать счётчик при повторной отправкеНе оставлять код в журналахКонтроль по источнику

Рис. 11: Меры для кода аутентификации. Сочетайте разрядность и срок с контролем попыток и эксплуатацией.

10. Четыре части вопроса 2 — различить на одной странице

Точки вопроса 2, которые легко смешать, упорядочены по значению, которое контролировал злоумышленник.

Атака Значение, которое сменил злоумышленник Чему не следовало доверять Устранение причины
Подделка JWT alg заголовка JWT, идентификатор в payload Алгоритм проверки, который объявляет сам токен Зафиксировать допустимые алгоритмы на стороне сервера
Чтение чужих сведений mid запроса Целевой идентификатор, указанный клиентом Сверять с субъектом JWT или определять целевой идентификатор из JWT
Повышение до платного status вне спецификации Все автоматически связанные свойства Сделать обновляемые свойства списком разрешённых
Подбор 4-значного кода Кандидаты otp Неограниченные попытки аутентификации Добавить лимиты попыток, задержки и решения о риске

Важно не сваливать всё это в «проверить ввод».

  • alg — криптографическая политика.
  • mid — авторизация на уровне объекта.
  • status — авторизация на уровне свойства.
  • otp — устойчивость к онлайн-угадыванию.

Даже внутри одного HTTP-запроса причина защищать каждое из них разная.

11. Вопрос 3(1) — подтвердить выполнение внешнего кода, не нанося вреда

После запуска сервиса в библиотеке H, широко используемой открытой библиотеке, публикуют критическую уязвимость V. Последовательность событий в задании такова.

  1. Злоумышленник кладёт строку с JNDI Lookup в HTTP-заголовок и отправляет её.
  2. Целевой сервер записывает это значение в журнал.
  3. Уязвимая библиотека вычисляет JNDI Lookup и запрашивает LDAP-сервер злоумышленника.
  4. Ответ LDAP возвращает URL HTTP-сервера злоумышленника.
  5. Целевой сервер получает файл класса и выполняет команду.

С скрытым именем конкретного продукта это читается как атака типа Log4Shell (CVE-2021-44228). Собственное описание Apache тоже представляет уязвимость так: если злоумышленник контролирует сообщения журнала или параметры, он может выполнить произвольный код, загруженный с LDAP-сервера.9

Поток подтверждения уязвимости типа Log4ShellБезвредный callback подтверждает, проходит ли цепочка от JNDI до выполнения внешнего кодаВнедрить jndi:ldap://...в x-api-versionJNDI LookupОтвет с HTTP URLЗаписать GETПодтвердить достижимостьЗлоумышленникУязвимый серверОбработка журналаВредоносный LDAP-серверВредоносный HTTP-серверindex.htmlТестовый сервержурнал доступаУязвимость подтверждена

Рис. 12: Поток подтверждения уязвимости типа Log4Shell. Достижимость подтверждают записью HTTP-обращения, а не разрушающей командой.

Проверочный код вызывает только безвредное HTTP-обращение

Компания G запускает проверочный код, который не влияет на систему, чтобы подтвердить, можно ли эксплуатировать уязвимость V извне. Единственная команда, которую выдаёт проверочный код, — получить index.html тестового сервера.

Вопрос 3(1) спрашивает, что нужно реализовать на тестовом сервере, чтобы подтвердить, что команда выполнена.

Образцовый ответ:

Механизм, который записывает обращения к index.html тестового сервера и позволяет их подтвердить

Если журнал доступа веб-сервера фиксирует GET с целевого сервера, это как минимум подтверждает, что прошла следующая цепочка.

Внешний HTTP-запрос
  -> обработка журнала
  -> JNDI Lookup
  -> ответ LDAP
  -> получение класса
  -> выполнение проверочной команды
  -> HTTP-обращение к тестовому серверу

Почему «показать текст на экране» недостаточно

Цель атаки — сервер. Нет гарантии, что на экране браузера пользователя что-то изменится. Кроме того, даже там, где уязвимость есть, исходящую связь на полпути может остановить межсетевой экран.

Запись обращения на стороне тестового сервера даёт наблюдаемое доказательство, что целевой сервер действительно вышел во внешний мир.

Выполняя такую проверку на практике, всегда соблюдайте следующее.

  • Получите явное разрешение владельца целевой системы.
  • Используйте метод проверки без влияния на промышленную среду или с приемлемо малым влиянием.
  • Не используйте разрушающие команды вроде записи, удаления или смены конфигурации.
  • Управляйте проверочным доменом и сервером сами.
  • Записывайте время проверки, источник, цель и ожидаемый callback.
  • После проверки разберите временные LDAP- или HTTP-серверы и учётные данные.

«Подтвердить, что возможно выполнение произвольного кода» и «выполнить произвольный опасный код» — не одно и то же. Держите побочные эффекты на минимуме, нужном для цели.

12. Вопрос 3(2)(3) — WAF проверяет HTTP-заголовок

WAF сервиса N позволяет выбрать объектом проверки GET, POST, PUT, ANY, Header, COOKIE или Multipart.

Код атаки попадает в значение HTTP-заголовка x-api-version. Поэтому поля e и f в таблице 6 оба равны Header.

Место из текста напрямую сопоставить с объектом проверки WAF

Здесь меньше общих знаний и больше чтения потока данных в тексте задания.

Где лежит строка атаки:
  заголовок x-api-version
          |
          v
Объект проверки WAF:
  Header

Это не параметр GET и не тело POST. Вместо того чтобы смотреть на список возможностей WAF и выбирать ANY, потому что «похоже на атаку», отвечайте местом, куда, по тексту задания, злоумышленник положил значение.

Учёт смены регистра

Первое предложение концептуально следовало такому правилу.

Header  \Wjndi\W  блокировать
Header  \Wldap\W  блокировать

Но смена регистра, как в jNdI, обходит шаблон, который совпадает только с нижним регистром.

Образцовый ответ на вопрос 3(3) — любой из следующих.

\W[jJ][nN][dD][iI]\W
\W(j|J)(n|N)(d|D)(i|I)\W

В билете обратная косая черта в японском начертании может выглядеть как знак иены, но как регулярное выражение это \W. \W совпадает с любым символом, кроме букв, цифр и подчёркивания. В синтаксисе JNDI Lookup сразу до и после jndi появляются символы вне класса word — вроде ${ и : — и шаблон написан так, чтобы ловить и их.

Ту же идею можно применить, чтобы сделать сторону ldap тоже нечувствительной к регистру.

\W[lL][dD][aA][pP]\W

Не считать это регулярное выражение «полной защитой от Log4Shell»

Экзамен просит регулярное выражение, которое закрывает приём обхода, показанный в тексте задания. Реальные атаки могут включать разбиение строк, альтернативные Lookup, кодирование, другие протоколы и вариации, которые одними сигнатурами покрыть трудно.

Практическое место поэтому такое.

  1. Временно блокировать уже известные шаблоны атак с помощью WAF.
  2. Выяснить, действительно ли затронутая библиотека присутствует.
  3. Ограничить исходящие LDAP, RMI и ненужный HTTP-трафик.
  4. Обновить до исправленной версии.
  5. После обновления всё равно проверить журналы и расследовать, был ли взлом.

WAF — слой, который покупает время до появления исправленной версии.

Место WAFWAF - слой временного смягчения, устранение причины - обновить библиотеку до исправленной версииПравило WAFобнаружение/блокировкаОграничить исходящий трафикОпубликована критическая уязвимостьПодтвердить воздействиеВременное смягчениеВременно остановить шаблон атакиЗакрыть путь эксплуатацииОбновить до исправленной библиотекиПоследующий разбор и предотвращение повторения

Рис. 13: Место WAF. WAF только покупает время до патча; устранение причины — обновление.

13. Вопрос 3(4) — почему начинать с «обнаружения»

По обновлённому правилу WAF зарегистрированный специалист Z советует на фиксированный срок после выхода в промышленную эксплуатацию поставить режим «обнаружение», а не «блокировка».

Вопрос спрашивает, не более чем в 25 знаках каждое, о пользе режима обнаружения и о том, что делать, чтобы минимизировать ущерб.

Образцовый ответ:

Пункт Суть ответа
Польза Можно предотвратить блокировку из-за ложного срабатывания
Что делать При получении оповещения разбирать, атака ли это

Режим обнаружения — не режим «ничего не делать»

В режиме обнаружения трафик, совпавший с правилом, всё равно пропускают, но записывают в журнал и поднимают оповещение. Даже если строка jndi или ldap случайно появится внутри легитимного вызова API, работу сразу не останавливают.

Взамен со стороны эксплуатации нужно следующее.

Получено оповещение
   |
   v
Проверить соответствующий запрос
   |
   +-- Легитимный трафик -> сузить правило, рассмотреть исключение
   |
   +-- Атака             -> изолировать цель, сохранить журналы, расследовать воздействие, перейти к блокировке

Если оповещения никто не смотрит, у режима обнаружения нет защитного эффекта вовсе. Обнаружение работает только в паре с процессом, который наблюдает и судит.

Путь от обнаружения к блокировке

Типичная процедура внедрения такова.

  1. Прогнать режим обнаружения по реальному трафику.
  2. Классифицировать срабатывания как ложные или истинные.
  3. Настроить целевой заголовок, путь, API, границы слов и так далее.
  4. Подтвердить, что влияние на легитимный трафик приемлемо.
  5. Переключиться в режим блокировки.
  6. Следить за числом блокировок и влиянием на работу.

Это, однако, принцип обычного времени. Когда уязвимость критична, её уже активно эксплуатируют и альтернативы нет, простой от взлома могут счесть тяжелее простоя от ложного срабатывания и сразу выбрать блокировку. В сценарии экзамена сначала выбирают режим обнаружения, чтобы подтвердить, что сервис может работать как прежде.

От режима обнаружения WAF к режиму блокировкиНаблюдать оповещения в режиме обнаружения, настроить ложные срабатывания, затем перейти к блокировкеРеальный трафикЛожноеАтакаРежим обнаруженияПоднято оповещениеАтака илиложное срабатываниеНастроить правилоПерейти в режим блокировкиСледить за числом блокировок и влиянием на работу

Рис. 14: От обнаружения к блокировке. Сначала наблюдать и настраивать, подтвердить приемлемое влияние, затем перейти к блокировке.

14. WAF — временная мера, обновление — устранение причины

В задании на официальном сайте библиотеки H ещё не было ни исправления, ни временного обхода, и даже комплексное правило WAF облачного провайдера должно было занять до 72 часов. Поэтому компания G сама подтверждает воздействие и временно блокирует хотя бы уже выявленные шаблоны.

Эта последовательность — базовая форма реагирования на инцидент.

Этап Цель Ответ в этом задании
Подтвердить воздействие Решить, действительно ли организация в опасности Подтвердить эксплуатируемость извне безвредным callback
Временное смягчение Купить время до исправления Правила WAF, обнаружение/блокировка, ограничения исходящего трафика
Устранение причины Убрать уязвимую причину Обновить до исправленной библиотеки
Последующий разбор Проверить, не эксплуатировали ли уже Исследовать журналы WAF, приложения, DNS, прокси и другие
Предотвратить повторение Ускорить следующее решение Перечень зависимостей, SBOM, процедура обновления, канал связи

«Не знаем, используем ли» — главный источник задержки

В задании даже когда компания G спрашивает компанию F, использует ли та библиотеку H, ответ занимает время, потому что нужен подробный разбор конфигурации.

На практике, если искать JAR-файлы начинают только после публикации критической уязвимости, реакция запаздывает. Как минимум следующее должно быть на месте в мирное время.

  • Перечень прямых и транзитивных зависимостей.
  • Компоненты и версии, реально входящие в поставку.
  • На какие сервисы, контейнеры и устройства они развёрнуты.
  • Процедура обновления зависимой библиотеки и пересборки/повторной поставки.
  • Канал связи для утверждения экстренных изменений.
  • Разрешённые назначения исходящего трафика и влияние их блокировки.
  • Где хранятся журналы и как по ним искать.

SBOM — не цель сама по себе. Это индекс, чтобы за короткое время ответить: «на какие работающие системы влияет эта уязвимость».

Не прекращать расследование после обновления

Вас могли уже атаковать около времени публикации уязвимости. Обновление до исправленной версии останавливает будущую эксплуатацию, но не стирает уже скомпрометированные учётные данные и уже установленный бэкдор.

Для уязвимости типа Log4Shell исследуйте как минимум следующие аспекты.

  • HTTP-запросы с подозрительными строками, указывающими на JNDI или LDAP.
  • Связь с сервера приложений наружу к LDAP, RMI или HTTP.
  • Запуск необычных дочерних процессов.
  • Создание подозрительных JAR, классов, скриптов или исполняемых файлов.
  • Доступ к облачным учётным данным или переменным окружения.
  • Аутентификацию, смену прав и исходящие передачи около времени обновления.

Важно не заключать «нас не атаковали» только по журналам WAF. Есть внутренние пути, которые никогда не проходят через WAF, и журналы, которые раньше не сохраняли.

15. Способ чтения, который облегчает набор баллов на экзамене

Это задание меньше проверка знаний и больше упражнение в чтении разрыва между спецификацией и реализацией.

15.1 Отделить «спецификацию» от «реализации» в таблицах

В проблеме status значение, которого нет в спецификации API, проходит в реализации.

Спецификация:
  mid / name / age

Реализация:
  отправить все полученные параметры в P

Когда этот разрыв виден, становится ясно, что поле c — общий модуль P.

15.2 Подчеркнуть значение, которое сменил злоумышленник

Значение, изменённое в каждой атаке, таково.

  • alg заголовка JWT
  • Идентификатор пользователя в payload JWT
  • Параметр API mid
  • status вне спецификации
  • otp API аутентификации
  • HTTP-заголовок x-api-version

Почти каждый вопрос спрашивает «где это значение следует проверять».

15.3 Вернуть ответ к терминам самого текста задания

На практике это можно назвать «BOLA», «Mass Assignment» и «rate limiting». Но вопрос просит конкретную обработку, сопоставленную со структурой текста.

Плохой пример:

Выполнять авторизацию надлежащим образом.

Хороший пример:

Проверить, совпадает ли идентификатор пользователя в JWT со значением mid.

Плохой пример:

Принять меры против перебора.

Хороший пример:

Заблокировать учётную запись, когда число последовательных неудач превышает порог.

Одно знание абстрактного имени не даёт ответа, который можно оценить в пределах лимита знаков.

15.4 Для WAF проследить «куда положили»

Объект проверки WAF решается не угадыванием по типу атаки, а по месту, куда положили строку атаки.

Положили в заголовок x-api-version
        ↓
Объект проверки — Header

Замечание комментария к оцениванию, что доля верных ответов в вопросе 3(1) была несколько ниже, тоже потому, что многие ответы не совпали с потоком атаки на рисунке 6. Уже одно перерисовать последовательность атаки стрелками показывает, что нужно наблюдать.

16. Чек-лист для реальных ревью API

Чек-лист, чтобы перенести это задание в реальные ревью дизайна и кода.

Проверка JWT

  • Допустимый алгоритм подписи зафиксирован в конфигурации сервера.
  • none и неожиданные алгоритмы отклоняются.
  • Подпись, iss, aud, exp и nbf проверяются в соответствии со сценарием.
  • ID-токены, токены доступа и токены обновления не путают друг с другом.
  • Есть процедура ротации ключей и отзыва.
  • Сведения, которые должны оставаться конфиденциальными, не кладут в payload JWT.

Авторизация на уровне объекта

  • Смена идентификатора внутри запроса не даёт дойти до чужих данных.
  • Авторизация применяется одинаково для списка, карточки, обновления, удаления и скачивания.
  • Авторизация применяется в общем слое, который доходит до данных, а не на экране.
  • Для API только о себе рассматривали вывод целевого идентификатора из токена.
  • Операции администратора используют отдельную политику от API обычного пользователя.

Авторизация на уровне свойства

  • Внешний тип ввода и сущность базы разделены.
  • Обновляемые поля перечислены списком разрешённых.
  • Свойства вне спецификации отклоняются или аудируются.
  • Состояния вроде прав, оплаты, согласования и владения нельзя менять из пользовательского ввода.
  • Ответы тоже исключают ненужные конфиденциальные свойства.

Попытки аутентификации

  • Есть лимит счётчика неудач по учётной записи.
  • Есть ступенчатая задержка и контроль по источнику.
  • Счётчик неудач не сбрасывается при повторной выдаче кода.
  • Код аутентификации можно использовать только один раз.
  • Коды аутентификации и пароли не оставляют в журналах.
  • Процедура разблокировки/восстановления сама не является более слабым путём аутентификации.

Критические уязвимости зависимых библиотек

  • Работающие сервисы можно сопоставить с версиями зависимостей.
  • Есть процедура проверки воздействия безвредным методом.
  • Можно применить временные меры вроде правил WAF и ограничений исходящего трафика.
  • Есть процесс эксплуатации, в котором ответственный просматривает оповещения обнаружения.
  • Есть аварийный путь выпуска для обновления до исправленной версии.
  • Журналы исследуют на возможную эксплуатацию до обновления.

17. Две стороны общих компонентов, как их показывает это задание

В этом задании два общих компонента: библиотека управления JWT Q и общий модуль P.

У общих компонентов большие плюсы.

  • Исправление в одном месте распространяется на все API, которые его используют.
  • Логику авторизации и проверки не нужно дублировать в каждую функцию.
  • Цели тестирования можно собрать вместе.
  • Форматы журналов и аудита можно унифицировать.

С другой стороны, ошибки тоже расходятся по всей системе.

  • Если библиотека Q принимает alg=none, уязвимым становится каждое API, которое использует JWT.
  • Если общий модуль P принимает произвольный mid или status, уязвимыми становятся и GET, и PUT.
  • Если уязвимая библиотека H используется в основании, каждый путь, который пишет HTTP-заголовки в журнал, становится частью поверхности атаки.

Значит выносить в общее нужно не просто доступ к данным. Нужно выносить в общий компонент сами инварианты безопасности и строго проверять этот компонент изолированно.

Например, сделайте контракт P таким.

P.getOwnUser(authenticatedSubject)
P.updateOwnProfile(authenticatedSubject, ProfileUpdate{name, age})

Безопаснее не выставлять низкоуровневый API вроде следующего напрямую обычным вызывающим.

P.getUser(arbitraryMid)
P.updateUser(arbitraryMap)

Последнее нужно только ограниченному набору маршрутов, например административной обработке. Раздать эту низкоуровневую свободу всем API даёт проект, который зависит от того, что каждый вызывающий каждый раз использует её правильно.

18. Итог

Задание 1 части PM весны 2024 года разбирает темы безопасности API по одной.

Использовать JWT не значит, что аутентификация безопасна. Если злоумышленник выбирает алгоритм подписи, идентификатор пользователя можно переписать.

Действительная подпись JWT не значит, что авторизация верна. Доверие mid запроса позволяет законному пользователю обратиться к чужим сведениям.

Уметь обновлять свой объект не значит, что можно менять каждое свойство. Автоматическое связывание внутреннего состояния вроде status позволяет переписать права или платёжный статус.

Срок истечения у кода аутентификации не значит, что он устойчив к перебору. Нужно посчитать число вариантов и скорость попыток и ограничить число неудач.

Положить правило в WAF не значит, что уязвимость исправлена. Обнаружение и блокировка только покупают время; подтверждают воздействие и в итоге обновляют библиотеку.

Соответствие уязвимостей мерамСопоставляет каждую уязвимость с её границей доверия и меройПроверка токенаАвторизация на уровне объектаАвторизация на уровне свойстваКонтроль попыток аутентификацииОт ввода к исполнениюПодделка JWTЗафиксировать допустимый алгоритмПодмена midСверить с субъектом JWT / mid не нуженstatus=paidСделать DTO обновления списком разрешённыхПеребор 4-значного кодаЛимит неудач / задержкаУязвимость типа Log4ShellОбновление библиотеки / WAF

Рис. 15: Соответствие уязвимостей мерам. Разделяйте исправление по тому, какая граница сломана.

Через всё это задание проходит один принцип.

Никогда не делать успех предыдущей проверки причиной пропустить следующую границу доверия.

Предыдущие статьи этой серии разбирают хранимый XSS в задании 1 части PM осени 2023 и вынос данных через гостевой Wi-Fi в задании 2 части PM осени 2023. О том, что проверять на сайте в целом, см. также как пользоваться документом IPA «Как создать безопасный веб-сайт» как чек-листом.

Итоговый выводПоказывает, что успех предыдущей проверки никогда не причина пропустить следующую границу доверияАутентификация успешнаПроверка подписи JWTАвторизация на уровне объектаАвторизация на уровне свойстваЛимит попытокГраница от ввода к исполнениюWAF / обновление библиотеки

Рис. 16: Итоговый вывод. Границы доверия проверяют поэтапно, ни одну нельзя пропустить.

Справочные ссылки

  1. IPA, Билет части PM экзамена SC весны 2024 года. Текст задания, на котором построена статья. ↩

  2. IPA, Образцовые ответы части PM экзамена SC весны 2024 года. Официальный образцовый ответ на каждый вопрос. ↩

  3. IPA, Комментарий к оцениванию части PM экзамена SC весны 2024 года. Объяснение долей верных ответов и типичных ошибок. ↩

  4. NIST, SP 800-63B: Authentication and Authenticator Management. Задаёт разрядность коротких секретов, лимиты попыток, счётчик неудач при повторной выдаче и отказ от почты для внеканальной аутентификации, среди прочих требований. ↩ ↩2

  5. RFC Editor, RFC 7519: JSON Web Token (JWT). Спецификация JWT, включая Unsecured JWT и alg=none. ↩

  6. RFC Editor, RFC 8725: JSON Web Token Best Current Practices. BCP, которое задаёт фиксацию множества допустимых алгоритмов и проверку издателя, субъекта и audience, среди прочего. ↩

  7. OWASP, API1:2023 Broken Object Level Authorization. Объясняет необходимость проверять авторизацию для каждого идентификатора объекта, который указывает пользователь. ↩

  8. OWASP, API3:2023 Broken Object Property Level Authorization. Объясняет пробелы авторизации на уровне свойства, включая Mass Assignment, и меры против них. ↩

  9. Apache Logging Services, Security. Объясняет воздействие CVE-2021-44228, выполнение кода через JNDI и LDAP и исправленные версии. ↩

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Разбор вопроса 2 дневного сеанса экзамена специалиста по обеспечению информационной безопасности, осень 5-го года Рэйва — файлы уносят через гостевой Wi-Fi

На материале вопроса 2 дневного сеанса экзамена специалиста по обеспечению информационной безопасности осенью 5-го года Рэйва (2023) стат...

Разбор задания 1 второй половины дня экзамена специалиста по обеспечению информационной безопасности, осень 5-го года Рэйва (2023) — хранимый XSS, из-за которого из 16 отзывов видно только 2

Разбор задания 1 второй половины дня экзамена специалиста по обеспечению информационной безопасности (осень 5-го года Рэйва, 2023): как о...

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Почему можно выдать себя за другого, если проверка подписи JWT прошла успешно?
В этом задании библиотека JWT принимала alg из заголовка так, как указал злоумышленник, и считала JWT с alg=none корректным вообще без подписи. Поэтому переписанный идентификатор пользователя в payload тоже проходит проверку. Ответ экзамена: проверять alg в заголовке JWT и убедиться, что значение не NONE. На практике отклонять только NONE недостаточно. Зафиксируйте допустимые алгоритмы — например RS256 — в конфигурации сервера и не выбирайте алгоритм по тому, что объявил сам токен. По сценарию также проверяйте issuer, audience, срок действия, subject и остальные нужные поля.
Достаточно ли для авторизации сравнить идентификатор пользователя внутри JWT с mid запроса?
Для ответа на экзамене — да. Если общий модуль P проверяет, что идентификатор в JWT совпадает с mid, атака с чужим mid останавливается. Но для API, которое работает только со своими данными вызывающего, на практике безопаснее вовсе не принимать mid от клиента и брать идентификатор из subject уже проверенного JWT. Маршруты вроде GET /users/me или PUT /users/me сами по себе снижают риск, что сравнение просто забудут написать. API, через которое администратор работает с другим пользователем, выносят на отдельную конечную точку со своей политикой авторизации.
Почему обычная проверка входных значений не останавливает атаку, которая добавляет status?
Потому что проверка длины name или диапазона age ничего не даёт, если status — поле, которое вообще не следовало принимать, — автоматически связывается и уходит во внутренний объект. Дело не в формате значения, а в авторизации на уровне свойства: можно ли пользователю менять это поле. В типе ввода для обновления оставьте только name и age и отклоняйте неизвестные свойства. Платёжный статус должен меняться только из событий, которым сервер доверяет, например из успешного результата платёжного сервиса.
Четырёхзначный код аутентификации истекает через 10 минут. Почему он всё равно опасен?
Потому что кандидатов от 0000 до 9999 всего 10 000, и при 10 попытках в секунду в среднем хватает 5 000 попыток — то есть 500 секунд. Срок действия 10 минут — это 600 секунд, поэтому, перебирая неповторяющиеся кандидаты по порядку, за это окно можно проверить 6 000 из них. Одного истечения мало, чтобы остановить перебор. Число кандидатов, скорость попыток и потолок числа попыток нужно закладывать вместе.
На экзамене мера — блокировка учётной записи. На практике достаточно ли сразу блокировать?
Нет. В поле ответа входит обработка, которая блокирует учётную запись, когда число последовательных неудач превышает порог. Но одна жёсткая постоянная блокировка позволяет злоумышленнику намеренно заблокировать чужую учётную запись и устроить отказ в обслуживании. На практике сочетают счётчик неудач по учётной записи, ступенчатые паузы, оценку риска источника и устройства, уведомления и процедуру восстановления. Важно и то, чтобы выдача нового кода не сбрасывала счётчик неудач в ноль.
Зачем ставить WAF в режим обнаружения, а не сразу в блокировку?
Чтобы легитимный рабочий трафик не останавливался, даже если обычная строка ошибочно принята за атаку. В образцовом ответе преимущество — можно не допустить блокировку из-за ложного срабатывания, а делать нужно следующее: получив оповещение, разобрать, атака это или нет. Режим обнаружения — не настройка «оставить и забыть». Это период наблюдения: смотрят журналы, отсеивают ложные срабатывания, правят правило и затем переходят к блокировке. Если известная критическая уязвимость уже реально эксплуатируется, можно сравнить это с риском доступности и сразу выбрать блокировку.
Библиотека H в этом задании — это Log4j?
В тексте имя продукта скрыто, но цепочка атаки — JNDI Lookup, LDAP-сервер, получение класса с HTTP-сервера, строка в HTTP-заголовке и высокий базовый балл CVSS v3.1 — естественно читается как абстракция CVE-2021-44228, известной как Log4Shell. Статья объясняет это соответствие, но на экзамене называть конкретный продукт не нужно. Ответить можно по данной процедуре атаки и спецификации WAF.
Что из этого задания стоит взять в практику?
Что успешная аутентификация, целостность JWT, право обратиться к объекту и право менять свойство — это разные проверки. Плюс короткому коду аутентификации нужен лимит попыток, а при критической уязвимости библиотеки подтверждение воздействия, временную защиту и устранение причины ведут параллельно. На практике центр тяжести такой: собрать авторизацию в общих компонентах, сделать схему ввода списком разрешённых полей, зафиксировать условия проверки JWT на стороне сервера и знать зависимости так, чтобы их можно было обновить.

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог