Добавить в корзинуПозвонить
Найти в Дзене
ОК

JSON Web Token: руководство для разработчиков API

В этой статье вы познакомитесь с концепцией JSON Web Token (JWT), поймёте его структуру и принципы работы. Вы узнаете, как применять JWT для аутентификации и авторизации в современных API, изучите основные преимущества и потенциальные уязвимости токенов. Мы рассмотрим практические примеры использования JWT в реальных приложениях и предоставим рекомендации по безопасной реализации этой технологии. JSON Web Token (JWT) — это открытый стандарт (RFC 7519) для создания токенов доступа, основанный на формате JSON. Обычно JWT используется для передачи данных после аутентификации в клиент-серверных приложениях. Токены создаются сервером, подписываются секретным ключом и передаются клиенту, который в дальнейшем использует этот токен для подтверждения своей личности при обращении к защищённым ресурсам. JWT можно представить как своеобразное «удостоверение личности» в цифровом мире. Когда пользователь входит в систему, сервер проверяет его учётные данные и, если они верны, выдаёт JWT. Этот токен
Оглавление
Часть 3.3 курса — «Аутентификация и авторизация»
Часть 3.3 курса — «Аутентификация и авторизация»

В этой статье вы познакомитесь с концепцией JSON Web Token (JWT), поймёте его структуру и принципы работы. Вы узнаете, как применять JWT для аутентификации и авторизации в современных API, изучите основные преимущества и потенциальные уязвимости токенов. Мы рассмотрим практические примеры использования JWT в реальных приложениях и предоставим рекомендации по безопасной реализации этой технологии.

Что такое JSON Web Token

JSON Web Token (JWT) — это открытый стандарт (RFC 7519) для создания токенов доступа, основанный на формате JSON. Обычно JWT используется для передачи данных после аутентификации в клиент-серверных приложениях. Токены создаются сервером, подписываются секретным ключом и передаются клиенту, который в дальнейшем использует этот токен для подтверждения своей личности при обращении к защищённым ресурсам.

JWT можно представить как своеобразное «удостоверение личности» в цифровом мире. Когда пользователь входит в систему, сервер проверяет его учётные данные и, если они верны, выдаёт JWT. Этот токен содержит информацию о пользователе и имеет цифровую подпись, которая позволяет проверить его подлинность. Благодаря этому при последующих запросах к серверу пользователю не нужно повторно вводить свои учётные данные — достаточно предоставить полученный ранее токен.

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

История и стандартизация

История JWT началась в 2011 году, когда была сформирована группа JOSE (JSON Object Signing and Encryption group), целью которой была стандартизация механизмов защиты целостности, шифрования и форматов ключей для обеспечения совместимости служб безопасности, использующих формат JSON.

К 2013 году появились первые неофициальные наработки, а официальная стандартизация JWT произошла в мае 2015 года группой IETF. С тех пор JWT стал широко применяться в веб-разработке для реализации безопасных механизмов аутентификации и авторизации.

Структура JWT

JWT состоит из трёх отдельных частей, разделённых точками (.):

Заголовок (Header)

Заголовок содержит метаданные о типе токена и используемом алгоритме шифрования. Обычно включает два поля:

  • alg: алгоритм, используемый для подписи/шифрования (например, HS256, RS256)
  • typ: тип токена, обычно "JWT"

Пример заголовка:

{
"alg": "HS256",
"typ": "JWT"
}
-2

Заголовок кодируется с помощью алгоритма Base64-URL и становится первой частью JWT.

Полезная нагрузка (Payload)

Полезная нагрузка содержит утверждения (claims) — фактические данные, которые передаются в токене. Это могут быть стандартные поля и пользовательские данные.

Стандартные поля (Registered Claim Names) включают:

  • iss (Issuer): идентификатор стороны, выдавшей JWT
  • sub (Subject): идентификатор пользователя или субъекта токена
  • aud (Audience): получатели, для которых предназначен токен
  • exp (Expiration Time): время истечения срока действия токена
  • nbf (Not Before): время, до которого токен не должен приниматься
  • iat (Issued At): время выдачи токена
  • jti (JWT ID): уникальный идентификатор токена

Пример полезной нагрузки:

{
"sub": "1234567890",
"name": "Иван Петров",
"admin": true,
"iat": 1516239022
}
-3

Полезная нагрузка также кодируется с помощью Base64-URL и становится второй частью JWT.

Подпись (Signature)

Подпись создаётся путём объединения закодированного заголовка и полезной нагрузки с секретным ключом, используя алгоритм, указанный в заголовке. Для алгоритма HS256 (HMAC SHA-256) это выглядит так:

HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)\text{HMACSHA256}(
\text{base64UrlEncode(header)} + "." +
\text{base64UrlEncode(payload)},
\text{secret}
)

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

Итоговый токен представляет собой три закодированные части, соединённые точками:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ItCY0LLQsNC9INCf0LXRgtGA0L7QsiIsImFkbWluIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjJ9.N78Poh8g1xWHMCDFbV8PzmcF3NfZkjwG0UV_re_KQ1o

Принцип работы JWT в API

Процесс использования JWT в API обычно состоит из следующих этапов:

1. Аутентификация и получение токена

  1. Пользователь отправляет учётные данные (логин и пароль) на сервер.
  2. Сервер проверяет учётные данные и, если они верны, создаёт JWT с необходимыми утверждениями (claims).
  3. Сервер отправляет токен клиенту в ответе.

2. Хранение токена

Клиент сохраняет токен для последующего использования. Существует два основных способа хранения:

  • В DOM-хранилище (localStorage, sessionStorage)
  • В cookies с флагом HttpOnly для защиты от XSS-атак

3. Использование токена для запросов

При каждом последующем запросе к защищённым ресурсам API клиент включает JWT в заголовок Authorization:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

4. Проверка токена сервером

Когда сервер получает запрос с токеном:

  1. Извлекает токен из заголовка Authorization.
  2. Проверяет подпись, используя свой секретный ключ.
  3. Проверяет срок действия и другие утверждения в токене.
  4. Если токен валиден, предоставляет доступ к запрашиваемому ресурсу.

На диаграмме представлен полный цикл работы JWT в API:

  1. Клиент аутентифицируется → Сервер проверяет учётные данные
  2. Сервер генерирует JWT → Клиент получает и сохраняет JWT
  3. Клиент отправляет запрос с JWT → Сервер проверяет JWT
  4. Сервер возвращает запрошенные данные → Клиент обрабатывает полученные данные

Преимущества использования JWT

JSON Web Tokens предоставляют ряд существенных преимуществ по сравнению с традиционными подходами к аутентификации:

Бессессионность (Statelessness)

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

Кросс-доменное взаимодействие

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

Децентрализованная проверка

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

Повышение производительности

В JSON-токенах можно хранить дополнительную полезную информацию о пользователях, что устраняет необходимость в дополнительных запросах к базе данных для получения этой информации.

Универсальность

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

Безопасность JWT и возможные атаки

Несмотря на все преимущества, использование JWT требует внимания к аспектам безопасности. Рассмотрим основные уязвимости и атаки на JWT:

Атаки на алгоритм подписи

Атака «Нет алгоритма»

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

Изменение алгоритма с RS256 на HS256

Атакующий может изменить алгоритм с асимметричного (RS256) на симметричный (HS256), заставив сервер использовать публичный ключ как секретный для проверки подписи.

Кража токена

Если JWT хранится в localStorage или sessionStorage, токен может быть украден через XSS-атаку, так как JavaScript имеет доступ к этим хранилищам. Для предотвращения рекомендуется использовать cookie с флагом HttpOnly.

Отсутствие механизма отзыва

JWT не имеет встроенного механизма для отзыва отдельных токенов. Если токен украден, он будет действителен до истечения срока его действия, если не реализованы дополнительные меры, такие как «чёрный список» токенов.

SQL-инъекции через JWT

Если в коде присутствует уязвимость, позволяющая выполнить SQL-запрос на основе данных из JWT без должной валидации, это может привести к SQL-инъекции.

Рекомендации по безопасному использованию JWT

Для безопасного использования JWT рекомендуется следовать этим практикам:

  1. Устанавливайте короткий срок действия токенов — это уменьшит окно возможности атаки в случае компрометации токена.
  2. Реализуйте механизм обновления токенов (refresh tokens) для более длительных сессий.
  3. Используйте безопасное хранение токенов — предпочтительно в HttpOnly cookies для защиты от XSS-атак.
  4. Проверяйте все утверждения в токене, включая срок действия, издателя и аудиторию.
  5. Используйте сильные алгоритмы подписи, такие как HS256, RS256 или ES256.
  6. Защищайте секретные ключи — обращайтесь с ними как с конфиденциальной информацией и регулярно их обновляйте.
  7. Всегда проверяйте наличие и валидность подписи — отклоняйте токены без подписи или с некорректной подписью.
  8. Реализуйте «чёрный список» токенов для возможности отзыва скомпрометированных токенов до истечения их срока действия.

Практические примеры использования JWT

Пример 1: Авторизация в REST API

Рассмотрим процесс авторизации пользователя в REST API с использованием JWT:

1. Пользователь отправляет запрос на аутентификацию с учётными данными:

POST /api/auth/login
Content-Type: application/json

{
"username": "user123",
"password": "securePassword"
}
-4

2. Сервер проверяет учётные данные и, если они верны, генерирует JWT:

HTTP/1.1 200 OK
Content-Type: application/json

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600
}
-5

3. Клиент использует полученный токен для доступа к защищённым ресурсам:

GET /api/users/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
-6

4. Сервер проверяет токен и, если он валиден, возвращает запрошенные данные:

HTTP/1.1 200 OK
Content-Type: application/json

{
"id": 123,
"username": "user123",
"email": "user@example.com",
"role": "user"
}
-7

Пример 2: Микросервисная архитектура

В микросервисной архитектуре JWT может использоваться для аутентификации запросов между различными сервисами:

  1. Пользователь аутентифицируется через сервис аутентификации и получает JWT.
  2. При запросе к сервису А клиент включает JWT в заголовок.
  3. Сервис А проверяет JWT и может извлечь из него информацию о пользователе.
  4. Если сервису А нужно обратиться к сервису Б от имени пользователя, он передаёт тот же JWT.
  5. Сервис Б также может самостоятельно проверить JWT, не обращаясь к сервису аутентификации.

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

Реализация JWT в различных технологиях

JWT поддерживается множеством языков программирования и фреймворков. Вот несколько примеров реализации:

Node.js (Express)

Для работы с JWT в Express можно использовать библиотеку jsonwebtoken:

const jwt = require('jsonwebtoken');
const secretKey = 'ваш_секретный_ключ';

// Создание токена
app.post('/login', (req, res) => {
// Проверка учётных данных
const user = authenticate(req.body);

if (user) {
const token = jwt.sign(
{ id: user.id, username: user.username, role: user.role },
secretKey,
{ expiresIn: '1h' }
);

res.json({ token });
} else {
res.status(401).json({ message: 'Неверные учётные данные' });
}
});

// Middleware для проверки токена
const verifyToken = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];

if (!token) {
return res.status(403).json({ message: 'Токен не предоставлен' });
}

try {
const decoded = jwt.verify(token, secretKey);
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ message: 'Недействительный токен' });
}
};

// Защищённый маршрут
app.get('/profile', verifyToken, (req, res) => {
res.json({ user: req.user });
});
-8

Python (Django REST Framework)

В Django REST Framework можно использовать djangorestframework-simplejwt:

# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}

# urls.py
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)

urlpatterns = [
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

# views.py
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def protected_view(request):
return Response({'user_id': request.user.id, 'username': request.user.username})
-9

Заключение

JSON Web Tokens представляют собой мощный и гибкий механизм для реализации аутентификации и авторизации в современных веб-приложениях. Их бессессионная природа делает их особенно полезными в масштабируемых и распределённых системах, таких как микросервисные архитектуры.

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

Понимание принципов работы JWT и потенциальных уязвимостей позволит вам эффективно использовать этот инструмент, обеспечивая при этом высокий уровень безопасности ваших приложений.

Практические упражнения

  1. Базовое упражнение: Создайте простой сервер аутентификации, выдающий JWT-токены при успешном входе пользователя.
  2. Упражнение среднего уровня: Реализуйте механизм обновления токенов (refresh tokens) для обеспечения длительных сессий без компрометации безопасности.
  3. Продвинутое упражнение: Создайте систему с несколькими микросервисами, использующими JWT для аутентификации и авторизации взаимных запросов.

Вопросы для самопроверки

  1. В чём основное отличие JWT от традиционной сессионной аутентификации?
  2. Какие три части составляют структуру JWT?
  3. Какие алгоритмы подписи обычно используются для JWT?
  4. Почему не рекомендуется хранить конфиденциальную информацию в JWT?
  5. Как реализовать механизм отзыва JWT-токенов?

Мы надеемся, что эта статья поможет вам лучше понять концепцию JWT и эффективно применять её в ваших проектах. Если у вас возникнут вопросы или вы захотите поделиться своим опытом использования JWT, пишите в комментариях. Наша команда всегда готова помочь вам разобраться в сложных вопросах веб-безопасности.