Каков стандартный процесс валидации Bearer-токена на стороне сервера?

«Каков стандартный процесс валидации Bearer-токена на стороне сервера?» — вопрос из категории Безопасность, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Стандартный процесс валидации Bearer-токена (чаще всего JWT) на стороне сервера включает несколько обязательных шагов для обеспечения безопасности.

Алгоритм проверки:

  1. Извлечение токена. Сервер должен извлечь токен из заголовка запроса Authorization. Формат заголовка: Authorization: Bearer <token>.

  2. Проверка подписи (Signature Verification). Это самый важный шаг. Сервер использует секретный ключ (для симметричных алгоритмов, например, HS256) или публичный ключ (для асимметричных, например, RS256), чтобы убедиться, что токен не был изменен после выдачи. Если подпись неверна, токен немедленно отклоняется.

  3. Проверка стандартных полей (Claims Validation). Сервер проверяет служебные поля (claims) внутри токена:

    • exp (Expiration Time): Убедиться, что срок действия токена не истек.
    • nbf (Not Before): Убедиться, что токен уже начал действовать.
    • iss (Issuer): Проверить, что токен выдан доверенным источником.
    • aud (Audience): Проверить, что токен предназначен для данного сервиса.
  4. (Опционально) Проверка в черном списке. Если система поддерживает досрочный выход пользователя (logout), идентификатор токена (claim jti) можно проверить по базе данных отозванных токенов (например, в Redis), чтобы убедиться, что он все еще действителен.

Пример на Python с FastAPI и PyJWT:

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
import jwt

SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        # Шаги 2 и 3: декодирование проверяет подпись и exp
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM], audience="my_app")
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except jwt.PyJWTError:
        # Ошибка подписи, срока действия и т.д.
        raise credentials_exception
    return {"username": username}