Справочник анонима. Как работают токены аутентификации и в чем их отличия от паролей
Содержание статьи
Справочник анонима
Статьи из этого цикла публикуются бесплатно и доступны всем. Мы убеждены, что каждый имеет право на базовые знания о защите своих данных.
Другие статьи цикла:
Если для тебя эти материалы тривиальны — отлично! Но ты сделаешь доброе дело, отправив ссылку на них своим друзьям, знакомым и родственникам, менее подкованным в технических вопросах.
Подписываемся под данными
И людям, и программам нужно знать, что данные были созданы доверенным источником и остались неизменными. Для этого была придумана технология генерации специального хеша (подписи), который подтверждает целостность информации и достоверность ее отправителя/создателя. Для создания этой самой подписи используется схема из нескольких шагов, цель которых — защитить данные от подделки.

Xakep #247. Мобильная антислежка
Алгоритм хеширования может меняться, но суть этого подхода проста и неизменна: для подтверждения целостности сообщения необходимо снова найти подпись защищаемых данных и сравнить ее с имеющейся подписью.
Придумываем коды доступа
Люди, которые придумали двухфакторную аутентификацию, по всей видимости, руководствовались принципом «одна голова хорошо, а две — лучше». И действительно — два пароля точно безопаснее одного. Но пароли, которые отправляет сайт в SMS, нельзя назвать абсолютно защищенными: сообщение чаще всего можно перехватить. Другое дело — специальные приложения для аутентификации, они нацелены на полную защиту всего процесса входа пользователя в аккаунт. Именно их мы сейчас с тобой и разберем.
Создание безопасных одноразовых паролей состоит из двух этапов:
В таком случае пользователь с помощью приложения, доступного на любом устройстве, сможет генерировать коды в соответствии со всеми стандартами.
Первоначальная настройка приложения заключается в обмене секретным ключом между сервером и приложением для аутентификации. Затем этот секретный ключ используется на устройстве клиента, чтобы подписать данные, которые известны и серверу, и клиенту. Этот ключ и служит главным подтверждением личности пользователя при вводе пароля на сервере.
На самом деле весь секрет — последовательность из случайных символов, которые закодированы в формате Base32. Суммарно они занимают не меньше 128 бит, а чаще и все 190 бит. Эту последовательность и видит пользователь как текст или QR-код.


Как приложение создает одноразовые коды? Все просто: приложение с помощью ключа хеширует какое-то значение, чаще всего число, берет определенную часть получившегося хеша и показывает пользователю в виде числа из шести или восьми цифр.
С самого начала для этого числа разработчики использовали простой счетчик входов. Сервер считал количество раз, которое ты заходил, например, на сайт, а приложению было известно, сколько раз ты запрашивал одноразовый пароль. Именно это значение и использовалось для создания каждого следующего одноразового кода. В современных приложениях вместо счетчика берется текущее время — и это намного удобнее для пользователя: счетчики входов часто сбивались, и их приходилось настраивать заново.
Теперь давай попробуем посчитать код для авторизации самостоятельно. Для примера представим, что мы решили прямо в Новый год опубликовать фотографию красивого фейерверка и, чтобы это сделать, нужно войти в свой аккаунт, а значит, нам не обойтись без одноразового пароля.
Входим в приложение
Ни одно современное приложение не спрашивает пароль у пользователя постоянно, поскольку пользователей это раздражает. Именно поэтому разработчики и ученые-криптографы придумали токены, которые могут заменить собой пару логин — пароль. Перед учеными стояла задача не столько скрыть какую-то информацию, сколько создать общий стандарт для ее хранения и подтверждения ее надежности. Для всего этого была придумана технология JSON Web Token (JWT).
Как работает JWT?
Если есть данные, достоверность которых следует подтвердить, нам надо подписать их секретным ключом, используя HMAC. Для этого применяется такой же способ хеширования, что и для одноразовых паролей, только вместо шести цифр берется весь хеш целиком. Единственная разница — это сам алгоритм хеширования: в таких токенах SHA-1 считают слишком коротким и небезопасным, поэтому обычно используют SHA-256.
Главная задача JWT — подтверждение личности создателя токена и сопутствующих данных. Обычно содержимое токена — логин или другой идентификатор пользователя.
Давай попробуем создать свой токен. Продолжим нашу маленькую историю с публикацией фотографии фейерверка в соцсети: мы ввели одноразовый пароль, сервер подтвердил нашу личность и хочет выдать токен, чтобы мы смогли с его помощью открыть наше приложение.
Любой токен состоит из трех частей: заголовка со служебной информацией, данных и подписи. Так как стандартом безопасности считается SHA-256, то мы запишем его в наш заголовок.
Внутри самого токена будет храниться информация об идентификаторе аккаунта, в который мы только что вошли.
Этот хеш нам тоже надо перевести в кодировку Base64 и затем присоединить к уже имеющейся строке из заголовка и данных: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjogMTIzNDU2fQ.4Ka0ipYe4/x-s4r82xqO8i77BXLh1TM7hdsqpmkZ6Y4 — это и есть наш токен. Можно пользоваться!

Подробнее про стандарт JWT можно почитать на сайте организации RFC, а про реализацию для своего любимого языка — на сайте jwt.io.
Заключение
Теперь ты знаешь, что происходит каждый день, когда ты открываешь браузер и заходишь в какой-нибудь веб-сервис. Понимая, как это работает, ты сможешь лучше защитить свои данные, а возможно, даже решишь применить какой-то из этих методов в своих разработках.
Авторизация и аутентификация для всех и каждого, часть 2
Перевод второй части статьи «Authorization and Authentication For Everyone».
В первой части мы рассмотрели основные термины, а также подробно остановились на теме делегированного доступа. Продолжаем разбираться.
Проблема входа в систему
После того как OAuth 2.0 обеспечил возможность доступа к сторонним API, создатели приложений также захотели, чтобы их пользователи могли логиниться при помощи других аккаунтов. Возьмем пример. Скажем, приложение HireMe123 хочет, чтобы пользователь MyCalApp мог логиниться в HireMe123, используя аккаунт MyCalApp — несмотря на то, что в самом HireMe123 у него нет аккаунта.
Но, как уже говорилось, OAuth 2.0 — это про делегированный доступ. Это НЕ протокол аутентификации. Тем не менее, это не остановило людей в их попытках использовать OAuth 2.0 именно с целью аутентификации, и это породило проблемы.
Проблемы, связанные с использованием токенов доступа для аутентификации
Если HireMe123 полагает, что успешный вызов API MyCalApp при помощи токена доступа означает, что пользователь может считаться аутентифицированным в HireMe123, мы сталкиваемся с проблемами. Дело в том, что у нас нет возможности проверить, был ли токен доступа выпущен для данного человека.
Эта проблема получила название confused deputy. HireMe123 не знает, откуда пришел токен и для кого он был выпущен. Давайте припомним: аутентификация — это проверка, действительно ли пользователь является тем, за кого себя выдает. То, что HireMe123 может использовать токен доступа для доступа к API, не дает ему оснований полагать, что пользователь действительно является тем, кем назвался.
Как уже говорилось, это никого не остановило и люди продолжали использовать OAuth 2.0 и токены доступа не по назначению (т. е., для аутентификации). В связи с этим быстро стала очевидной необходимость формализовать аутентификацию поверх OAuth 2.0, чтобы позволить логиниться при помощи сторонних приложений и при этом не допускать пробоев в безопасности.
OpenID Connect
Это привело нас к спецификации под названием OpenID Connect (OIDC).
OIDC это спецификация поверх OAuth 2.0, которая говорит, как аутентифицировать пользователей. Стандарты OIDC разрабатываются OpenID Foundation (OIDF).
OIDC — это слой идентификации для аутентификации пользователей при помощи сервера авторизации.
Вы помните, что сервер авторизации выпускает токены. Токены — это закодированные кусочки данных для передачи информации между разными сторонами (такими как сервер авторизации, приложение или API). В случае OIDC и аутентификации сервер авторизации выпускает ID-токены.
ID-токены
ID-токены предоставляют информацию о событии аутентификации и идентифицируют пользователя. ID-токены предназначены для клиента. Они имеют фиксированный формат, понятный для клиента: клиент может извлечь идентифицирующую информацию из токена и таким образом аутентифицировать пользователя.
OIDC декларирует фиксированный формат для ID-токенов —
JSON Web Token (JWT)
JSON Web Tokens (JWT, иногда произносится как «джот») составляются из трех URL-безопасных строковых сегментов, соединенных точками.
Заголовок JWT
Первый сегмент токена это заголовок. Он может выглядеть примерно так:
Заголовок токена это JSON-объект, содержащий алгоритм подписи и тип токена. Он зашифрован при помощи алгоритма base64Url (бинарные данные, представленные в виде текста).
В расшифрованном виде это выглядит примерно так:
Полезная нагрузка JWT
Второй сегмент токена — полезная нагрузка. Выглядеть этот сегмент может так:
Это объект JSON, содержащий заявления (claims) — предложения о пользователе и событии аутентификации. Например:
Этот сегмент тоже base64Url-зашифрован.
Крипто-сегмент
Последний сегмент — это подпись или данные шифрования. JWT подписываются, чтобы их нельзя было модифицировать при пересылке. Когда сервер авторизации выпускает токен, он подписывает его при помощи ключа.
Получая ID-токен, клиент проверяет подпись (тоже при помощи ключа).
(При использовании асимметричного алгоритма для подписи и проверки используются разные ключи. В таком случае только у сервера авторизации есть возможность подписывать токены).
Не переживайте, если все это кажется запутанным. Детали того, как все работает, не должны вас беспокоить или мешать эффективно использовать сервер авторизации с аутентификацией на основе токенов.
Заявления
Теперь, когда мы знаем анатомию JWT, давайте остановимся на заявлениях (claims) — тех самых предложениях из сегмента полезной нагрузки токена. Как следует из самого их названия, ID-токены предоставляют идентифицирующую информацию, а содержится она в заявлениях.
Заявления аутентификации
Начнем с предложений, касающихся события аутентификации. Вот несколько примеров таких заявлений:
Среди необходимых заявлений об аутентификации в ID-токенах можно назвать:
nonce привязывает запрос авторизации клиента к получаемому токену. По сути это криптографически случайная строка, которую клиент создает и отсылает вместе с запросом авторизации. Сервер авторизации помещает nonce в токен, который отсылает назад приложению. Приложение проверяет, совпадает ли nonce в токене с тем, который был послан с запросом авторизации. Таким образом приложение может проверить, что токен поступил именно из того места, откуда запрашивался.
Заявления идентичности
Заявления также включают предложения о конечном пользователе. Вот несколько примеров таких заявлений:
Среди стандартных заявлений профиля в ID-токенах можно назвать:
Итак, мы прошли краткий курс по важным спецификациям (OAuth 2.0 и OpenID Connect). Теперь давайте посмотрим, как можно применить эти знания в работе.
Аутентификация при помощи ID-токенов
Давайте посмотрим, как происходит OIDC-аутентификация.
Примечание. Учитывайте, что это упрощенная схема. В зависимости от архитектуры вашей системы у вас будет несколько других потоков.
Рассмтариваемые нами сущности: браузер, приложение, запущенное в браузере, и сервер авторизации. Когда пользователь хочет войти в систему (залогиниться в приложении), это приложение отсылает запрос авторизации на сервер авторизации. Пользовательские логин и пароль проверяются сервером авторизации. Если все сходится, сервер выпускает ID-токен для приложения.
Затем клиентское приложение расшифровывает ID-токен (JWT) и проверяет его. В проверку входит проверка подписи, а также заявлений:
Когда мы установили аутентичность ID-токена, пользователь считается аутентифицированным. Также у нас есть доступ к заявлениям идентичности, благодаря чему мы знаем, кем является наш пользователь.
Итак, пользователь аутентифицирован. Время взаимодействовать с API.
Доступ к API при помощи токенов доступа
Выше мы уже немного поговорили о токенах доступа — когда рассматривали, как работает делегированный доступ с использованием OAuth 2.0 и серверов авторизации. Теперь давайте разберем все это подробнее. Для этого вернемся к нашему сценарию с HireMe123 и MyCalApp.
Токены доступа
Токены доступа используются для предоставления доступа к ресурсам. Благодаря токену доступа, выпущенному сервером авторизации MyCalApp, HireMe123 может получить доступ к API MyCalApp.
В отличие от ID-токенов, которые OIDC определяет как JSON веб-токены, токены доступа не имеют четко определенного формата. Они не обязательно являются JWT. Тем не менее, во многих решениях для токенов доступа все же используются JWT, потому что этот формат позволяет валидацию.
Токены доступа непрозрачны для клиента
Токены доступа предназначаются для API ресурса и важно, чтобы они были непрозрачны для клиента. Почему?
Токены доступа могут измениться в любой момент. У них должно быть короткое время устаревания, чтобы пользователь мог часто получать новые. Также они могут выпускаться повторно, чтобы дать возможность доступа к другим API или с другими правами. В клиентском приложении ни в коем случае не должно быть кода, полагающегося на содержимое токена доступа. Подобный код будет слишком хрупким и практически гарантированно сломается.
Доступ к API ресурса
Допустим, мы хотим использовать токен доступа для вызова API одностраничного приложения. Как это происходит?
Выше мы рассматривали процесс аутентификации. Предположим, что пользователь залогинился в наше JS-приложение в браузере. Это приложение отсылает запрос авторизации к серверу авторизации, запрашивая токен доступа для вызова API.
Затем, когда наше приложение хочет вступить во взаимодействие с этим API, мы прилагаем токен доступа к заголовку запроса, вот так:
Авторизованный запрос отсылается к API, который проверяет токен при помощи промежуточного ПО. Если все сходится, API возвращает данные (например, JSON) приложению, запущенному в браузере.
Это все хорошо, но выше мы упоминали, что OAuth решает проблему чрезмерных прав доступа. Как это происходит?
Делегирование с областью видимости
Как API узнает, какой уровень доступа нужно дать приложению? Это определяется путем установления области видимости (scopes).
Область видимости «ограничивает, что именно приложение может делать в интересах пользователя». Она не позволяет выдать права, которых у пользователя уже нет. Например, если пользователь MyCalApp не имеет права создавать новые корпоративные аккаунты, область видимости гарантирует, что HireMe123 тоже не позволит пользователю создавать новые корпоративные аккаунты.
Области видимости делегируют контроль доступа самому API или ресурсу. За соответствие областей видимости правам пользователя отвечает API.
Давайте рассмотрим это на примере.
Я использую приложение HireMe123. HireMe123 хочет получить доступ к стороннему API MyCalApp для создания события в календаре от моего имени. Приложение HireMe123 уже запросило токен доступа к MyCalApp на сервере авторизации MyCalApp. В этом токене содержится важная информация:
HireMe123 посылает запрос к API MyCalApp с токеном доступа в заголовке авторизации. Когда API MyCalApp получает этот запрос, он видит, что в нем установлена область видимости write:events.
Но на MyCalApp содержатся аккаунты с календарями сотен тысяч пользователей. Поэтому, чтобы убедиться, что этот запрос от HireMe123 будет касаться только моих прав создавать события в моем аккаунте, промежуточное ПО API MyCalApp должно проверить не только область видимости, но и sub — идентификатор субъекта.
В контексте делегированной авторизации области видимости обозначают, что именно приложение сможет делать от имени пользователя. Это подмножество прав пользователя в целом.
Проверка согласия
Помните, мы говорили, что сервер авторизации спрашивает пользователя HireMe123, согласен ли он разрешить HireMe123 воспользоваться правами пользователя для доступа к MyCalApp?
Этот диалог выглядит примерно так:
HireMe123 может попросить предоставить ему самые разные права, на пример:
В целом, следует избегать давать разрешения на чисто пользовательские действия. Области видимости предназначены для прав, делегированных приложениям. Но если ваш сервер авторизации имеет функционал для контроля доступа на основе ролей (Role-Based Access Control, RBAC), вы можете устанавливать разные области видимости для разных пользователей.
При помощи RBAC на сервере авторизации можно установить разные права для разных групп пользователей. После этого сервер авторизации при выпуске токенов доступа сможет включать в scopes указание ролей пользователей.
Итоги
Мы рассмотрели очень много материала, но это даже близко не все, что можно было рассмотреть. Тем не менее, я надеюсь, что этот краткий курс по идентичности, авторизации и аутентификации будет вам полезен.
Что такое JWT токен?
Эта статья посвящена детальному разбору JWT и его возможностей. Мы изучим структуру токена и построим его с нуля. Затем рассмотрим наиболее распространенные способы использования.
Во время разработки сервиса с пользователям типичной задачей является реализация авторизации.
Когда у вас один сервис, все довольно просто:
Эта строка обычно хранится в куках браузера, а потому называется авторизационной кукой.
Все было отлично, пока не пришли микросервисы. Допустим у нас есть сервер авторизации, который находится на домене auth.foo.com, и парочка других сервисов. Один из ваших сервисов находится на домене blog.foo.com.
Когда пользователь авторизуется на сервере авторизации, то кука будет записана для домена auth.foo.com. Сервис blog.foo.com уже не будет иметь доступ к этой куке.
Это можно исправить, если хранить куку в LocalStorage браузера. Тогда возникает другая проблема. У сервиса blog.foo.com нет возможности проверить куку, так как он ее не выдавал. Сервису необходимо сделать запрос на сервер авторизации, чтобы проверить существование куки и кому она принадлежит. Это создает нагрузку на сервер авторизации.
Решением всех этих проблемы является JWT токен (JSON Web Token), который возлагает задачу по обработке аутентификационных данных на сами микросервисы, а следовательно позволяет избежать различных ошибок авторизации, увеличить производительность и улучшить масштабируемость приложения.
Однако неправильное использование JWT может негативно сказаться на безопасности приложения. Разберем примеры использования JWT, разберем распространенные ошибки в реализации схем аутентификации с применением JWT, рассмотрим основные виды атак на эти схемы и дадим рекомендации по их предотвращению.
Формат JWT
JWT состоит из трех основных частей: заголовка (header), нагрузки (payload) и подписи (signature). Заголовок и нагрузка формируются отдельно в формате JSON, кодируются в base64, а затем на их основе вычисляется подпись. Закодированные части соединяются друг с другом, и на их основе вычисляется подпись, которая также становится частью токена.
Бывают и исключения, когда в JWT отсутствует подпись. Подобный случай будет рассмотрен далее.
Header
Заголовок является служебной частью и состоит из двух полей: типа токена, в данном случае JWT, и алгоритма хэширования подписи:
Значение поля typ зачастую игнорируется приложениями, однако стандарт не рекомендует отказываться от него для обеспечения обратной совместимости.
Поле alg обязательно для заполнения. В приведенном случае был применен алгоритм HS256 (HMAC-SHA256), в котором для генерации и проверки подписи используется единый секретный ключ.
Для подписи JWT могут применяться и алгоритмы асимметричного шифрования, например RS256 (RSA-SHA256). Стандарт допускает использование и других алгоритмов, включая HS512, RS512, ES256, ES512, none и др.
Использование алгоритма none указывает на то, что токен не был подписан. В подобном токене отсутствует часть с подписью, и установить его подлинность невозможно.
Закодируем этот JSON в base64 и получим: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload
Полезна нагрузка — это любые данные, которые вы хотите передать в токене. Стандарт предусматривает несколько зарезервированных полей:
Все эти claims не являются обязательными, но их использование не по назначению может привести к коллизиям.
Любые другие данные можно передавать по договоренности между сторонами, использующими токен. Например, payload может выглядеть так:
Поскольку набор полей в части полезной нагрузки произвольный, приложение может хранить в этой части практически любые данные. Например, для ускорения работы приложения в полезной нагрузке могут храниться Ф.И.О. пользователя, чтобы не запрашивать эти сведения каждый раз из базы данных.
Payload не шифруется при использовании токена, поэтому не стоит передавать в нем чувствительные данные. Например, паспортные данные.
Размер payload не ограничен, но не стоит передавать в нем много данных. Из-за размера токена могут начаться проблемы с производительностью.
У нас уже есть 2/3 токена, осталось сгенерировать подпись.
Signature
Подпись генерируется следующим образом: Закодированные заголовок и полезная нагрузка объединяются с точкой («.») в качестве разделителя. Затем эта строка хешируется указанным в header алгоритмом. Результат работы алгоритма хеширования и есть подпись.
В нашем примере токен будет выглядеть так: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBdXRoIFNlcnZlciIsInN1YiI6ImF1dGgiLCJleHAiOjE1MDU0Njc3NTY4NjksImlhdCI6MTUwNTQ2NzE1MjA2OSwidXNlciI6MX0.9VPGwNXYfXnNFWH3VsKwhFJ0MazwmNvjSSRZ1vf3ZUU
Другие сервисы знают пароль, которым авторизационный сервис подписывает токены. Или у них есть публичный ключ, которым они могут проверить подпись. Поэтому если кто-то попытается изменить данные в токене, то он станет не валидным. Например, если вы храните в токене роль пользователя, а злоумышленник попробует подставить туда “admin”, то он не сможет сгенерировать новую подпись для поддельного токена.
Официальный сайт jwt.io предлагает два алгоритма хэширования: HS256 и RS256. Но можно использовать любой алгоритм с приватным ключом.
Официальный сайт jwt.io предлагает два алгоритма хэширования: HS256 и RS256. Но можно использовать любой алгоритм с приватным ключом.
Аутентификация
Схема аутентификации с использованием JWT предельно проста.
Пользователь вводит свои учетные данные в приложении или доверенном сервисе аутентификации. При успешной аутентификации сервис предоставляет пользователю токен, содержащий сведения об этом пользователе.
При последующих обращениях токен передается приложению в запросах от пользователя: в cookie, заголовках запроса, POST или GET параметрах и т. д.
Получив токен, приложение сперва проверяет его подпись. Убедившись, что подпись действительна, приложение извлекает из части полезной нагрузки сведения о пользователе и на их основе авторизует его.
Можно реализовать всю эту схему вручную, а можно использовать одну из библиотек указанных на jwt.io.
Преимущества JWT
Перечислим преимущества использования JWT в сравнении с классической схемой аутентификации, использующей сессии.
Во-первых, подход с использованием токенов позволяет не хранить информацию обо всех выданных токенах, как при классической схеме. Когда пользователь обращается к приложению, он передает ему свой токен. Приложению остается только проверить подпись и извлечь необходимые поля из полезной нагрузки.
В-третьих, при использовании отдельного сервиса аутентификации становится возможным организовать единую точку входа в различные сервисы с одними и теми же учетными данными (SSO). Единожды пройдя процедуру аутентификации, пользователь сможет получить доступ со своим токеном к тем ресурсам, которые доверяют этому сервису аутентификации.
В-четвертых, как было указано ранее, приложение может хранить в части полезной нагрузки практически любые данные, что при грамотной архитектуре приложения может существенно увеличить производительность.
Благодаря перечисленным факторам схема аутентификации с использованием JWT широко используется в различных корпоративных приложениях. Особенно популярна эта схема в тех приложениях, которые реализуют парадигмы микросервисной архитектуры: при таком подходе каждый сервис получает необходимые ему сведения о пользователе непосредственно из токена, а не тратит время на получение этой информации из базы данных.
Уязвимости JWT
В этом разделе будут рассмотрены основные атаки на JWT и даны рекомендации по их предотвращению.
Перехват токена
Перехват пользовательского токена может привести к ряду неприятных последствий.
Во-первых, так как JWT передается в открытом виде, для получения хранящихся в части полезной нагрузки исходных данных достаточно применить к этой части функцию base64UrlDecode. То есть злоумышленник, перехвативший токен, сможет извлечь хранящиеся в токене данные о пользователе.
В соответствии с лучшими практиками для предотвращения подобной угрозы рекомендуется:
Во-вторых, злоумышленник, перехвативший токен, сможет его переиспользовать и получить доступ к приложению от лица пользователя, чей JWT был перехвачен.
Здесь рекомендации будут следующие:
Refresh tokens
В современных схемах аутентификации, основанных на JWT, после прохождения аутентификации пользователь получает два токена:
Access token при таком подходе имеет сильно ограниченное время жизни (например, одну минуту). Refresh token же имеет длительное время жизни (день, неделя, месяц), но он одноразовый и служит исключительно для обновления access token пользователя.
Схема аутентификации в таком случае выглядит следующим образом:
Подбор ключа симметричного алгоритма подписи
При использовании симметричных алгоритмов для подписи JWT (HS256, HS512 и др.) злоумышленник может попытаться подобрать ключевую фразу.
Подобрав ее, злоумышленник получит возможность манипулировать JWT-токенами так, как это делает само приложение, а следовательно сможет получить доступ к системе от лица любого зарегистрированного в ней пользователя.
В нашем примере из первой части статьи для подписи JWT в качестве ключевой фразы была использована строка password. Она простая, короткая и содержится во всех основных словарях для перебора паролей. Злоумышленнику не составит труда подобрать эту ключевую фразу с использованием программ John the Ripper или hashcat.
Рекомендации для защиты от атаки в этом случае такие:
Использование алгоритма none
Как было упомянуто в первой части статьи, использование в заголовке JWT алгоритма none указывает на то, что токен не был подписан. В подобном токене отсутствует часть с подписью, и установить его подлинность становится невозможно.
Рассмотрим подобную атаку на нашем примере. Наш токен в незакодированном виде выглядит следующим образом
Предположим, мы хотим, чтобы приложение считало нас администратором. Для этого необходимо установить значение admin в поле role полезной нагрузки. Но при внесении в токен этого изменения подпись токена станет невалидной, и приложение не примет такой JWT.
Для обхода защитного механизма мы можем попытаться изменить значение поля alg в заголовке токена на none. Наш токен примет следующий вид:
Поскольку мы используем алгоритм none, подпись отсутствует. В закодированном виде наш JWT будет выглядеть так: eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpZCI6IjEzMzciLCJ1c2VybmFtZSI6ImJpem9uZSIsImlhdCI6MTU5NDIwOTYwMCwicm9sZSI6ImFkbWluIn0
Этот токен мы и передадим на сервер. Уязвимое приложение, проверив заголовок JWT и обнаружив в нем alg: none, примет этот токен без всяких проверок, как если бы он был легитимным, в результате чего мы получим привилегии администратора.
Чтобы защититься от такой атаки:
Изменение алгоритма подписи
При использовании асимметричных алгоритмов подпись токена осуществляется с использованием приватного ключа сервиса, а проверка подписи — с использованием публичного ключа сервиса.
Некоторые реализации библиотек для работы с JWT содержат логические ошибки, заключающиеся в том, что при получении токена, подписанного с использованием симметричного алгоритма (например, HS256), для проверки подписи в качестве ключевой фразы будет использован публичный ключ сервиса. Поскольку публичный ключ сервиса не засекречен, злоумышленник может легко получить его и использовать для подписи собственных токенов.
Для рассмотрения примера этого варианта атаки нам понадобится новый JWT:
В кодированном виде он будет выглядеть следующим образом: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEzMzciLCJ1c2VybmFtZSI6ImJpem9uZSIsImlhdCI6MTU5NDIwOTYwMCwicm9sZSI6InVzZXIifQ.YLOVSKef-paSnnM8P2JLaU2FiS8TbhYqjewLmgRJfCj1Q6rVehAHQ-lABnKoRjlEmHZX-rufHEocDxGUYiGMjMexUQ3zt-WqZITvozJ4pkvbV-mJ1nKj64NmqaR9ZkBWtmF-PHJX50eYjgo9rzLKbVOKYOUa5rDkJPHP3U0aaBXFP39zsGdOTuELv436WXypIZBeRq2yA_mDH13TvzegWCK5sjD4Gh177bCq57tBYjhGIQrDypVe4cWBPlvwFlmG8tdpWGu0uFp0GcbTAfLUlbTSuGROj88BY0XeUs0iqmGlEICES3uqNx7vEmdT5k_AmL436SLedE0VHcyxve5ypQ
Поскольку в этом случае мы используем для подписи алгоритм RS256, нам понадобятся публичный и приватный ключи.
Публичный ключ Приватный ключ
Для тестов мы будем использовать сайт jwt.io

Как и в предыдущем примере, модифицируем токен:
В кодированном виде заголовок и зполезная нагрузка будут выглядеть следующим образом:
Остается только подсчитать подпись с использованием публичного ключа сервиса.
Для начала переводим ключ в hex-представление:
Затем генерируем подпись с использованием openSSL:
Полученное значение E1R1nWNsO-H7h5WoYCBnm6c1zZy-0hu2VwpWGMVPK2g добавляем к уже имеющейся строке, и наш токен принимает следующий вид: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEzMzciLCJ1c2VybmFtZSI6ImJpem9uZSIsImlhdCI6MTU5NDIwOTYwMCwicm9sZSI6ImFkbWluIn0.E1R1nWNsO-H7h5WoYCBnm6c1zZy-0hu2VwpWGMVPK2g
Подставляем в поле secret на jwt.io наш публичный ключ, и JWT успешно проходит проверку. Не забудьте поставить галочку secret base64 encoded.
Для предотвращения такой атаки рекомендуется:
Заключение
JSON Web Tokens — популярная и удобная технология. При правильном использовании JWT избавляет от распространенных ошибок недостаточной авторизации, позволяет просто и удобно распределить информационные потоки между сервисами, организовать единую точку входа в различные сервисы с одними и теми же учетными данными и даже повысить производительность сервиса.
Вместе с тем при неправильном использовании JWT можно подвергнуть свою систему существенным рискам, вплоть до компрометации учетных записей абсолютно всех пользователей системы.
Итак, для безопасного использования JWT следует:
После беглого знакомства с JSON web tokens может сложиться впечатление, что они встроены в современные механизмы авторизации и аутентификации, такие как OAuth или OpenID. Однако это не совсем так. JSON токены действительно используются в этих системах, но не являются их частью. Более того, сфера их использование гораздо шире авторизации.














