Шпаргалки по безопасности: JWT
Многие приложения используют JSON Web Tokens (JWT), чтобы позволить клиенту идентифицировать себя для дальнейшего обмена информацией после аутентификации.
JSON Web Token – это открытый стандарт (RFC 7519), который определяет компактный и автономный способ безопасной передачи информации между сторонами в виде объекта JSON.
Эта информация является проверенной и надежной, потому что она имеет цифровую подпись.
JWT могут быть подписаны с использованием секретного (с помощью алгоритма HMAC) или пары открытого / секретного ключей с использованием RSA или ECDSA.
JSON Web Token используется для передачи информации, касающейся личности и характеристик клиента. Этот «контейнер» подписывается сервером, чтобы клиент не вмешивался в него и не мог изменить, например, идентификационные данные или какие-либо характеристики (например, роль с простого пользователя на администратора или изменить логин клиента).
Этот токен создается в случае успешной аутентификации и проверяется сервером перед началом выполнения каждого клиентского запроса. Токен используется приложением в качестве “удостоверения личности” клиента (контейнер со всей информацией о нем). Сервер же имеет возможность проверять действительность и целостность токена безопасным способом. Это позволяет приложению быть stateless (stateless приложение не сохраняет данные клиента, сгенерированные за один сеанс для использования в следующем сеансе с этим клиентом (каждый сеанс выполняется независимо)), а процессу аутентификации независимым от используемых сервисов (в том смысле, что технологии клиента и сервера могут различаться, включая даже транспортный канал, хотя наиболее часто используется HTTP).
Соображения по поводу использования JWT
Даже если токен JWT прост в использовании и позволяет предоставлять сервисы (в основном REST) без сохранения состояния (stateless), такое решение подходит не для всех приложений, потому что оно поставляется с некоторыми оговорками, как, например, вопрос хранения токена.
Если приложение не должно быть полностью stateless, то можно рассмотреть возможность использования традиционной системы сессий, предоставляемой всеми веб-платформами. Однако для stateless приложений JWT – это хороший вариант, если он правильно реализован.
Проблемы и атаки, связанные с JWT
Использование алгоритма хеширования NONE
Подобная атака происходит, когда злоумышленник изменяет токен, а также меняет алгоритм хеширования (поле “alg”), чтобы указать через ключевое слово none, что целостность токена уже проверена. Некоторые библиотеки рассматривали токены, подписанные с помощью алгоритма none, как действительный токен с проверенной подписью, поэтому злоумышленник мог изменить полезную нагрузку (payload) токена, и приложение доверяло бы токену.
Для предотвращения атаки необходимо использовать библиотеку JWT, которая не подвержена данной уязвимости. Также во время проверки валидности токена необходимо явно запросить использование ожидаемого алгоритма.
Перехват токенов
Атака происходит, когда токен был перехвачен или украден злоумышленником и он применяет его для получения доступа к системе, используя идентификационные данные определенного пользователя.
Защита заключается в добавлении «пользовательского контекста» в токен. Пользовательский контекст будет состоять из следующей информации:
Если во время проверки токена полученный токен не содержит правильного контекста, он должен быть отклонен.
Пример реализации:
Код для создания токена после успешной аутентификации:
Код для проверки валидности токена:
Явное аннулирование токена пользователем
Поскольку токен становится недействительным только после истечения срока его действия, у пользователя нет встроенной функции, позволяющей явно отменить действие токена. Таким образом, в случае кражи пользователь не может сам отозвать токен и затем заблокировать атакующего.
Одним из способов защиты является внедрение черного списка токенов, который будет пригоден для имитации функции «выход из системы», существующей в традиционной системе сеансов.
В черном списке будет храниться сборник (в кодировке SHA-256 в HEX) токена с датой аннулирования, которая должна превышать срок действия выданного токена.
Когда пользователь хочет «выйти», он вызывает специальную службу, которая добавляет предоставленный токен пользователя в черный список, что приводит к немедленному аннулированию токена для дальнейшего использования в приложении.
Хранилище черного списка:
Для централизованного хранения черного списка будет использоваться база данных со следующей структурой:
Управление аннулированиями токенов:
Раскрытие информации о токене
Эта атака происходит, когда злоумышленник получает доступ к токену (или к набору токенов) и извлекает сохраненную в нем информацию (информация о токене JWT кодируется с помощью base64) для получения информации о системе. Информация может быть, например, такой как, роли безопасности, формат входа в систему и т.д.
Способ защиты достаточно очевиден и заключается в шифровании токена. Также важно защитить зашифрованные данные от атак с использованием криптоанализа. Для достижения всех этих целей используется алгоритм AES-GCM, который обеспечивает аутентифицированное шифрование с ассоциированными данными (Authenticated Encryption with Associated Data – AEAD). Примитив AEAD обеспечивает функциональность симметричного аутентифицированного шифрования. Реализации этого примитива защищены от адаптивных атак на основе подобранного шифртекста. При шифровании открытого текста можно дополнительно указать связанные данные, которые должны быть аутентифицированы, но не зашифрованы.
То есть шифрование с соответствующими данными обеспечивает подлинность и целостность данных, но не их секретность.
Однако необходимо отметить, что шифрование добавляется в основном для сокрытия внутренней информации, но очень важно помнить, что первоначальной защитой от подделки токена JWT является подпись, поэтому подпись токена и ее проверка должны быть всегда использованы.
Хранение токенов на стороне клиента
Если приложение хранит токен так, что возникает одна или несколько из следующих ситуаций:
Остается случай, когда злоумышленник использует контекст просмотра пользователя в качестве прокси-сервера, чтобы использовать целевое приложение через легитимного пользователя, но Content Security Policy может предотвратить связь с непредвиденными доменами.
Также возможно реализовать службу аутентификации таким образом, чтобы токен выдавался внутри защищенного файла cookie, но в этом случае должна быть реализована защита от CSRF.
Использование слабого ключа при создании токена
Если секрет, используемый в случае алгоритма HMAC-SHA256, необходимый для подписи токена, является слабым, то он может быть взломан (подобран c помощью атаки грубой силы). В результате злоумышленник может подделать произвольный действительный токен с точки зрения подписи.
Для предотвращения этой проблемы надо использовать сложный секретный ключ: буквенно-цифровой (смешанный регистр) + специальные символы.
Поскольку ключ необходим только для компьютерных вычислений, размер секретного ключа может превышать 50 позиций.
Для оценки сложности секретного ключа, используемого для вашей подписи токена, вы можете применить атаку по словарю паролей к токену в сочетании с JWT API.
jwt malformed #756
Comments
grenadecx commented Jun 13, 2016 •
Okay so I’m running laravel 5.2 and trying to generate a token that is valid on a socket.io server. However I seem to fail horrible at this, (not sure if this is a problem with me or this library) so some help would be much appreciated!
On my laravel I have some simple lines:
And then I use this token when making the socket io connection from the client:
Simple socketio server:
However this gives me:
So I instead of using socketio-jwt library, I also tried interfacing it directly against jsonwebsocket which gives me invalid signature as well.
I use the same key on both places and my jwt.php config file looks like this:
So what am I missing? I also tried to decode the jwt on the https://jwt.io/ website, but that doesn’t work either. Help is very much appreciated!
The text was updated successfully, but these errors were encountered:
tymondesigns commented Jun 13, 2016
grenadecx commented Jun 13, 2016
Sorry, I accidentally opened it without context, it’s added.
grenadecx commented Jun 13, 2016
And at some point I also got jwt malformed, just trying to replicate that. But any help would be appreciated.
grenadecx commented Jun 13, 2016
I just tried another library, https://github.com/lcobucci/jwt and I have no problems generating a token that will get validated from the socketio server.
Could this be an issue with the underlying namshi/jose library?
tymondesigns commented Jun 13, 2016
No problem.. reopened.. I’ll take a look when I can
tdhsmith commented Jun 15, 2016
Could you post an example of a failing JWT with its key?
grenadecx commented Jun 15, 2016 •
Edit:
Validating this key as from my understanding should work great over at jwt.io since it uses HS256 encryption. But it fails with invalid signature over there.
Here’s a simple validation directly against jsonwebtoken for nodejs that fails:
mcblum commented Jun 17, 2016
Does this sound like it could be the same as the issue I just submitted, #765?
tdhsmith commented Jun 17, 2016 •
@grenadecx Ok well I can confirm that jwt.io and jsonwebtoken don’t accept it for some reason, but I’m not sure why yet.
I manually verified that the signature looks valid based on the given payload and header, though:
I did find one big discrepancy: your payload has escaping backslashes before the forward slashes:
The extra slashes then disappear to javascript because / isn’t a metacharacter, which is an important point, but I don’t know why this would be relevant during the signature validation. (it should verify the signature of the base64-ed payload directly, not decode it to a string, then reencode it)
grenadecx commented Jun 17, 2016
Thanks for taking a look.
I’ve no idea why I have baskslashes in the url. I mean, I don’t do anything funky with it, I’m running the basic configuration file and all I do is the JWTAuth::fromUser function with a user and then insert the token with <<$token>> in the javascript.
How imporant is the iss? Seems like it can basically be anything and still give a valid signature?
tdhsmith commented Jun 17, 2016
Actually I’m thinking the backslash thing was a red herring. The way I calculated the signature was wrong, so it’s not a good sign that namshi/jose agreed with me.
iss isn’t actually checked by this library’s validators, but some services use it to identify where requests are coming from for example. If you’re building both ends of the communication, it isn’t required by any means. You’d have to take it out of your jwt.required_claims as well as call JWTAuth::factory()->setDefaultClaims to exclude it. But don’t do that yet because I don’t think that’s the problem anymore.
tdhsmith commented Jun 17, 2016
Do you know what version of namshi/jose you have installed? (run composer show namshi/jose )
It’s possible you’re hitting namshi/jose#36. It’s been fixed in this library since #126 (i.e. jwt-auth v0.5.4), but it’s possible you haven’t updated or are running a really old version. (What jwt-auth version are you running by the way?)
tdhsmith commented Jun 17, 2016
The odd thing—and the reason I think that’s the issue—is that your signature is the base64url encoded version of the HMAC hex string. But you should be taking the base64url of the HMAC’s actual binary value.
Otherwise it’s doing a weird conversion that’s basically useless (base64 is mostly about making binary data consumable as text, but hex is already text). It also explains why your signature was weirdly long (base64 is 4x denser than hex, and base64-ing hex makes it another 3/4ths as dense).
grenadecx commented Jun 18, 2016
Yeah I also found it weird that the token as so long compared to other libraries when generating the same amount of data.
I was using the documentation when installing just a few days ago, but I can try updating to 0.5.4 or whatever the latest is and see if that solves it.
I’ll report back in a bit 🙂
grenadecx commented Jun 18, 2016
Okay so I updated to tymon/jwt-auth 0.5.9 which updated the namshi/jose to 5.0.2 and it solved my problem.
Not sure why I was using an older version since the documentation says to install 0.5.*. I really appreciate the help and wish ya the best!
JWT — как безопасный способ аутентификации и передачи данных
JSON Web Token (JWT) — это открытый стандарт (RFC 7519) для создания токенов доступа, основанный на формате JSON. Как правило, используется для передачи данных для аутентификации в клиент-серверных приложениях. Токены создаются сервером, подписываются секретным ключом и передаются клиенту, который в дальнейшем использует данный токен для подтверждения своей личности.
В простом понимании — это строка в специальном формате, которая содержит данные, например, ID и имя зарегистрированного пользователя. Она передается при каждом запросе на сервер, когда необходимо идентифицировать и понять, кто прислал этот запрос.
В этой статье разберу, что такое Access токен, Refresh токен и как с ними работать.
Для дальнейших разборов будет использован токен:
После того, как посетитель прошел авторизацию в нашей системе, указав свой логин и пароль, система выдает ему 2 токена: access token и refresh токен.
После чего посетитель, когда хочет получить с сервера данные, например, свой профиль, вместе с запросом он передает Access токен, как на примере выше. Сервер, получив его проверяет, что он действительный (об этом чуть ниже), вычитывает полезные данные из него (тот же user_id) и, таким образом, может идентифицировать пользователя.
Токен разделен на три основные группы: заголовок, полезные данные и сигнатура, разделенные между собой точкой.
Это можно проверить прям в браузере, выполнив в консоле или js коде:
Вторым блоком идет eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9
Это есть полезные данные, так же закодированные в Base64. После раскодирования получим:
Поскольку необходимо ограничивать токен по времени, поле exp обязательно. По нему можно проверить, актуален ли токен или нет.
Она получается примерно следующим образом:
Берем заголовок, например <"alg":"HS256","typ":"JWT">и кодируем его в base64, получаем ту самую часть eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Тоже самое проделываем с данными eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9
После этого склеиваем их и получаем eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9
Далее эти данные шифруем с помощью нашего алгоритма HMAC-SHA256 и ключа.
Для проверка токена необходимо проделать ту же операцию.
Как только время выйдет, пользователю снова придется проходить авторизацию. Так вот чтобы этого избежать, существует Refresh токен. С помощью него можно продлить Access токен.
У него, обычно, нет какой-то структуры и это может быть некая случайная строка.
Для проекта odo24.ru я использовал следующий подход.
Генерируется Access токен и после случайная строка, например T6cjEbghMZmybUd_fhE
С нашего нового Access токена eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJleHAiOjE1ODEzNTcwMzl9.E4FNMef6tkjIsf7paNrWZnB88c3WyIfjONzAeEd4wF0 беру последние шесть знаков, получаю Ed4wF0
Склеиваю и получаю рефреш токен T6cjEbghMZmybUd_fhEEd4wF0
Это сделано для привязки Access токена к Refresh. Для получения новых токенов необходимо передать эти два токена. Делается проверка на их связку и только после валидируется Access токен. Если и второй этап прошел успешно, тогда получаем с базы данных по текущему user_id рефреш токен и сверяем с тем, что к нам пришел. Если они совпадают, тогда генерируются новые токены и в базе данных обновляется Refresh токен на новый.
В моем случае я разделил оба токена и храню в разных местах. Access токен нужен только для идентификации пользователя и на клиенте (JS) он не нужен, поэтому он передается в Cookie (http only).
Refresh токен хранится в LocalStorage и используется только когда Access токен перестал быть актуальным.
Представим ситуацию, когда у нас каким-то образом украли Access токен. Да, это уже плохо и где-то у нас брешь в безопасности. Злоумышленник в этом случае сможет им воспользоваться не более чем на 15-30 минут. После чего токен «протухнет» и перестанет быть актуальным. Ведь нужен второй токен для продления.
Если украли Refresh токен, то без Access токена (который недоступен в JS) продлить ничего нельзя и он оказывается просто бесполезным.
Дату протухания внедрил прям в токен с той целью, чтобы не хранить эту информацию где-то в другом месте, например, в базе данных.
Дата содержит год, месяц, день, час и минуты. Хранится в ASCII
Кодирование даты на Golang:
Всю реализацию на Go можно изучить на Github-е
В этой статье попытался рассказать о взаимодействии двух токенов и как ими пользоваться. В сети достаточно много информации о Access токенах, однако мало, как мне показалось, информации о Refresh токенах.
Атаки на JSON Web Tokens
Содержание:
Что такое JSON Web Token?
Веб-токен JSON обычно используется для авторизации в клиент-серверных приложениях. JWT состоит из трех элементов:
Заголовок
Это объект JSON, который представляет собой метаданные токена. Чаще всего состоит из двух полей:
Официальный сайт предлагает два алгоритма хэширования:
Но на самом деле любой алгоритм с приватным ключом может быть использован.
Полезная нагрузка
Это также объект JSON, который используется для хранения такой информации о пользователе, как:
Подпись
Это наиболее важная часть, поскольку она определяет целостность токена путем подписания заголовка и полезной нагрузки в кодировке Base64-URL, разделенных точкой (.) с секретным ключом. Например, чтобы сгенерировать токен с помощью алгоритма HS256, псевдокод будет таким:
Что такое SECRET_KEY?
Как правило, JWT может быть сгенерирован с помощью двух механизмов шифрования, таких как:
Симметричное шифрование:
Этот механизм требует единственного ключа для создания и проверки JWT.
Например, пользователь «Vasya» сгенерировал JWT с «h1dd1n_m1ss1g3» в качестве секретного ключа. Любой человек, знающий этот ключ, может с его помощью изменить токен. JWT при этом останется действительным.
Самый распространенный алгоритм для этого типа — HS256.
Асимметричное шифрование:
Этот механизм требует открытого ключа для проверки и закрытого ключа для подписи.
Например, если «Vasya» использовал это шифрование, то он единственный, кто может создать новый токен, используя закрытый ключ, тогда как «Petya» может только проверить токен с помощью открытого ключа, но не может его изменить.
Наиболее распространенный алгоритм для этого типа — RS256.
Атаки на JWT
Чтобы подделать токен, необходимо иметь правильные ключи (например, секретный ключ для HS256, открытый и закрытый ключи для RS256), но если конфигурация JWT не реализована правильно, то есть много способов обойти элементы управления, которые позволяют изменить токен и получить несанкционированный доступ.
Базовые атаки
Для выполнения всех этих атак нам понадобиться JWT_Tool
1. Нет алгоритма
Если приложению не удается проверить значение заголовка «alg», то мы можем изменить его значение на «none», и таким образом оно исключает необходимость действительной подписи для проверки. Например:
Команда:
Здесь jwt_tool создал различные полезные нагрузки для использования этой уязвимости и обхода всех ограничений, пропустив раздел «Подпись».
2. Изменяем алгоритм с RS256 на HS256
Как было сказано выше, алгоритму RS256 нужен закрытый ключ для подделки данных и соответствующий открытый ключ для проверки подлинности подписи. Но если мы сможем изменить алгоритм подписи с RS256 на HS256, мы заставим приложение использовать только один ключ для выполнения обеих задач, что является нормальным поведением алгоритма HMAC.
Таким образом рабочий процесс будет преобразован из асимметричного в симметричное шифрование. Теперь мы можем подписывать новые токены тем же открытым ключом.
Команда:
В данном случае мы сначала загружаем открытый ключ (public.pem) из приложения, а затем подписываем токен с помощью алгоритма HS256, используя этот ключ. Таким образом, мы можем создавать новые токены и вставлять полезную нагрузку в любое существующее утверждение.
3. Без проверки подписи
Иногда при фаззинге данных в разделе заголовка и полезной нагрузки приложение не возвращает ошибку. Это значит, что подпись не проверяется после того, как она была подписана сервером авторизации. Таким образом, мы можем вставить любую полезную нагрузку в заявку, а токен всегда будет действительным.
Команда:
Здесь часть подписи не проверяется, а значит можно смягчить утверждение «имени» в разделе полезной нагрузки, сделав себя «администратором».
4. Взлом секретного ключа
Мы можем получить доступ к файлу SECRET_KEY с помощью уязвимостей, таких как
Если это невозможно, то все равно можно провести другие атаки, чтобы проверить, использует ли токен какую-либо слабую секретную строку для шифрования.
Для этой цели можно использовать расширение BurpSuite под названием JWT Heartbreaker.
Такое раскрытие поставит под угрозу весь механизм безопасности, поскольку теперь мы можем генерировать произвольные токены с секретным ключом.
Но чтобы убедиться, что полученная нами строка является действительным ключом SECRET_KEY или нет? Мы можем использовать функцию Crack в jwt_tool.
Команда:
5. Использование произвольных файлов для проверки
Key ID (kid) – это необязательный заголовок, имеющий строковый тип, который используется для обозначения конкретного ключа, присутствующего в файловой системе или базе данных, а затем использования его содержимого для проверки подписи. Этот параметр полезен, если приложение имеет несколько ключей для подписи токенов, но может быть опасным, если он является инъекционным, поскольку в этом случае злоумышленник может указать на конкретный файл, содержимое которого предсказуемо.
Например, «/dev/null» называется нулевым файлом устройства и всегда ничего не возвращает, поэтому он отлично работает в системах на основе Unix.
Команда:
В качестве альтернативы можно использовать любой файл, присутствующий в корневом веб-каталоге, например, CSS или JS. Также можно использовать его содержимое для проверки подписи.
Другое решение проблемы:
Продвинутые атаки:
1. SQL-инъекция
Эта уязвимость может возникнуть, если любой параметр, который извлекает какое-либо значение из базы данных, не очищается должным образом. Благодаря чему можно решать CTF задачи.
Например, если приложение использует алгоритм RS256, но открытый ключ виден в заявлении «pk» в разделе Payload, тогда можно преобразовать алгоритм подписи в HS256 и создавать новые токены.
Команда для подсчета количества столбцов:
2. Параметр поддельного заголовка
JSON Web Key Set (JWKS) — это набор открытых ключей, которые используются для проверки токена. Вот пример:
Этот файл хранится на доверенном сервере, приложение может указывать на этот файл через параметры заголовка:
Но мы можем управлять URL-адресом с помощью таких уловок, как:
Затем мы можем перенаправить Приложение на наш вредоносный сервер вместо доверенного сервера и генерировать новые токены, так как мы контролируем как открытые, так и закрытые ключи.
JSON Set URL (jku):
Этот параметр указывает на набор открытых ключей в формате JSON (атрибуты n и e в JWKS), а «jwt_tool» автоматически создает файл JWKS с именем «jwttool_custom_jwks.json» для этой атаки при первом запуске инструмента после установки.
Команда:
X.509 URL (x5u):
Этот параметр указывает на сертификат открытого ключа X.509 или цепочку сертификатов (атрибут x5c в JWKS). Вы можете сгенерировать этот сертификат с соответствующим закрытым ключом следующим образом:
Здесь с использованием OpenSSL сертификат был создан в «attacker.crt», который теперь может быть встроен в файл JWKS с атрибутом «x5c», а его эксплуатация может осуществляться следующим образом:
Встроенные открытые ключи:
Если сервер встраивает открытые ключи непосредственно в токен с помощью параметров «jwk» (JSON Web Key) или «x5c» (цепочка сертификатов X.509), попробуйте заменить их своими собственными открытыми ключами и подписать токен соответствующим закрытым ключом.
3. Внедрение заголовка ответа HTTP
Предположим, что если приложение ограничивает любой управляемый URL-адрес в параметрах «jku» или «x5c», тогда мы можем использовать уязвимость внедрения заголовка ответа, чтобы добавить встроенный JWKS в ответ HTTP и заставить приложение использовать это для проверки подписи.
4. Прочие уязвимости
Веб-токены JSON – это еще одна форма пользовательского ввода, все параметры в которой должны быть очищены должным образом, иначе это может привести к уязвимостям, таким как:
Но это не означает, что приложение по-прежнему безопасно, ведь, если злоумышленник не может подделать JWT, он попытается украсть их с помощью неправильной конфигурации:



