Хук jwt помечает проверку JWT без закрепленного алгоритма, дыру подмены алгоритма (algorithm confusion), при которой проверяющий код доверяет заголовку alg из самого токена, а также полностью отключенную проверку. Он учитывает язык и читает каждый файл с учетом его собственной библиотеки JWT. Это детерминированный движок, стоящий за специализацией federation, и вы можете запускать его сами в режиме scan или check.
Запуск
- Сканировать кодовую базу
- Проверить вызов
npx lagune read jwt # scans the whole project
npx lagune read jwt --dir src # scans a directory
npx lagune read jwt --file auth.ts # scans a single file
npx lagune read jwt --lang javascript --pattern 'jwt.verify(token, secret)' # => unpinned
npx lagune read jwt --lang python --pattern 'jwt.decode(t, k, options={"verify_signature": False})' # => unpinned
npx lagune read jwt --lang python --pattern 'jwt.decode(t, k, algorithms=["HS256"])' # => safe
Каждая строка в разделе Unpinned JWT verification found представляет собой вызов проверки, который не задает список разрешенных алгоритмов algorithms (там, где библиотеке он нужен), принимает небезопасный список (none или асимметричный алгоритм вперемешку с HMAC) или вовсе отключает проверку подписи и поэтому доверяет alg из самого токена. Это подтвержденная уязвимость, поэтому хук завершается с ненулевым кодом. Если ничего не найдено, выводится no unpinned JWT verification found.
Как читать вердикт
| Вердикт | Значение |
|---|---|
unpinned | Вызов проверки без списка разрешенных алгоритмов (там, где он нужен), с небезопасным списком или с отключенной проверкой. |
safe | Вызов проверки, который закрепляет надежный список разрешенных алгоритмов или чья библиотека закрепляет его неявно. |
invalid | Во фрагменте нет узнаваемого вызова проверки, который можно оценить. |
Параметры CLI
| Параметр | Значение | Описание |
|---|---|---|
--pattern | фрагмент кода | Проверяет один вызов. Повторите, чтобы проверить несколько, по одному вердикту на строку. |
--lang | язык | Обязателен с --pattern: один из javascript, python, go, java, kotlin, php, ruby, rust, csharp. |
--dir | директория | Ограничивает сканирование директорией. Можно повторять и сочетать с --file. |
--file | файл | Ограничивает сканирование одним файлом. Можно повторять и сочетать с --dir. |
Без параметров хук сканирует весь проект. --pattern требует --lang и не сочетается с --dir или --file.
Поддерживаемые языки
Сканирование читает эти библиотеки, определяя язык по расширению файла. Некоторые из них закрепляют алгоритм неявно (отсутствие явного списка там безопасно), поэтому правило "нет списка разрешенных алгоритмов ⇒ unpinned" применяется только там, где библиотека ожидает явный список, и правильно настроенный вызов никогда не помечается.
| Язык | Библиотека | Помечает отсутствие списка как unpinned? | Помечает всегда |
|---|---|---|---|
| JavaScript / TypeScript | jsonwebtoken, jose | Да | none, смесь асимметричного алгоритма и HMAC |
| Python | PyJWT, python-jose | Да (PyJWT требует список, у python-jose по умолчанию algorithms=None) | none, verify_signature: False |
| Go | golang-jwt | Да (без WithValidMethods) | none, ParseUnverified |
| Ruby | ruby-jwt | Да | none, verify = false |
| Java / Kotlin | jjwt, nimbus | Нет (алгоритм выводится из ключа) | none, неподписанный или unsecured парсер |
| PHP | firebase/php-jwt | Нет (Key несет алгоритм) | none |
| Rust | jsonwebtoken | Нет (Validation требует список) | none, insecure_disable_signature_validation |
| C# | Microsoft.IdentityModel | Нет (параметры на нескольких строках) | none, RequireSignedTokens = false |
Хук решает только вопрос закрепления алгоритма: проверки claims, ключей и потока остаются вопросами суждения, которые по-прежнему охватывает специализация federation.
Частые вопросы
Проверяющий код, который читает алгоритм из заголовка токена, позволяет злоумышленнику заменить RS256 на HS256 и подписать токен открытым ключом. Закрепление надежного списка разрешенных алгоритмов в коде предотвращает это, а хук jwt помечает вызов проверки, который не задает список или задает небезопасный.
