Аутентификация API: API Key, OAuth 2.0, JWT, Basic Auth — сравнение методов

← Вернуться к статьям

Введение

Каждый API должен знать, кто отправляет запрос. Без аутентификации API открыт для всех — любой может читать данные, создавать ресурсы, удалять записи. Но какой метод выбрать?

API Key, Basic Auth, JWT, OAuth 2.0, HMAC — каждый решает проблему по-своему, с разным уровнем безопасности, сложности и удобства. В этой статье — честное сравнение всех методов: как работают, когда использовать и как реализовать.

Аутентификация ≠ Авторизация

Сначала аутентификация, потом авторизация. Эта статья — про аутентификацию.

📋 Содержание

1. API Key

Простой Серверные интеграции

Как работает

Сервер выдаёт клиенту уникальную строку (ключ). Клиент передаёт её с каждым запросом. Сервер проверяет ключ в базе данных.

# В заголовке (рекомендуется)
curl https://api.example.com/data \
  -H "X-API-Key: sk_live_abc123def456"

# В query-параметре (менее безопасно)
curl "https://api.example.com/data?api_key=sk_live_abc123def456"
Безопасность:
Средняя

Плюсы

  • Максимально простая реализация
  • Легко понять и использовать
  • Подходит для server-to-server
  • Легко ротировать

Минусы

  • Нет срока действия (пока не отзовут)
  • Один ключ = все разрешения
  • Опасно хранить на клиенте
  • Нет информации о пользователе

Реализация (Node.js)

const express = require('express');
const app = express();

function apiKeyAuth(req, res, next) {
  const key = req.headers['x-api-key'] || req.query.api_key;

  if (!key) {
    return res.status(401).json({ error: 'API key required' });
  }

  // Проверка в БД (используйте хеш, не храните ключи в открытом виде!)
  const client = await db.clients.findOne({
    where: { api_key_hash: crypto.createHash('sha256').update(key).digest('hex') }
  });

  if (!client) {
    return res.status(401).json({ error: 'Invalid API key' });
  }

  req.client = client;
  next();
}

app.use('/api', apiKeyAuth);

Кто использует: Google Maps, OpenWeatherMap, Stripe (для серверных запросов), SendGrid

2. Basic Auth

Простой Только HTTPS

Как работает

Клиент передаёт username:password в Base64-кодировке через заголовок Authorization:

# curl с Basic Auth
curl -u admin:secret123 https://api.example.com/users

# Эквивалент:
# Authorization: Basic YWRtaW46c2VjcmV0MTIz
# (base64 от "admin:secret123")
Безопасность:
Низкая (Base64 ≠ шифрование)

Base64 — это НЕ шифрование

Base64 легко декодируется. echo "YWRtaW46c2VjcmV0MTIz" | base64 -dadmin:secret123. Без HTTPS логин и пароль передаются фактически в открытом виде.

Плюсы

  • Встроен в HTTP-стандарт
  • Поддержка во всех HTTP-клиентах
  • Простейшая реализация

Минусы

  • Пароль отправляется каждый запрос
  • Нет срока действия
  • Опасен без HTTPS
  • Нет механизма отзыва

Когда использовать: внутренние сервисы, CI/CD, быстрые прототипы. Всегда через HTTPS.

3. Bearer Token

Средний Универсальный

Как работает

Клиент получает токен (обычно после логина) и передаёт его в заголовке Authorization: Bearer <token>:

Клиент: POST /login
{email, password}
Сервер проверяет
и выдаёт токен
Клиент сохраняет
токен
Все запросы с
Authorization: Bearer ...
# 1. Логин — получаем токен
curl -X POST https://api.example.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email": "ivan@test.ru", "password": "secret"}'
# {"token": "abc123xyz...", "expires_in": 3600}

# 2. Используем токен
curl https://api.example.com/me \
  -H "Authorization: Bearer abc123xyz..."
Безопасность:
Высокая

Bearer Token — это формат передачи, а не конкретная технология. Токеном может быть случайная строка, JWT или opaque-токен.

Кто использует: практически все современные API

4. JWT (JSON Web Token)

Средний Stateless

Как работает

JWT — это Bearer Token, который содержит данные внутри себя. Три части, разделённые точками:

eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo0Miwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzA4NzAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
 ↑ Header (alg, typ)      ↑ Payload (данные)                                    ↑ Signature (подпись)
// Payload (декодированный)
{
  "user_id": 42,
  "role": "admin",
  "email": "ivan@test.ru",
  "iat": 1708600000,
  "exp": 1708700000
}
Безопасность:
Высокая (с правильной реализацией)

Плюсы

  • Stateless — сервер не хранит сессии
  • Масштабируемость (нет общего хранилища)
  • Данные внутри токена (не нужен запрос в БД)
  • Кросс-доменная работа
  • Стандарт RFC 7519

Минусы

  • Нельзя отозвать до истечения
  • Payload видны всем (Base64)
  • Больший размер (~500-1000 байт)
  • Уязвимость при неправильном алгоритме

Реализация (Node.js)

const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET;

// Создание токена
app.post('/auth/login', async (req, res) => {
  const user = await validateCredentials(req.body);
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });

  const token = jwt.sign(
    { user_id: user.id, role: user.role, email: user.email },
    SECRET,
    { expiresIn: '1h' }
  );

  const refreshToken = jwt.sign(
    { user_id: user.id, type: 'refresh' },
    SECRET,
    { expiresIn: '30d' }
  );

  res.json({ token, refresh_token: refreshToken, expires_in: 3600 });
});

// Проверка токена (middleware)
function jwtAuth(req, res, next) {
  const auth = req.headers.authorization;
  if (!auth?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Bearer token required' });
  }

  try {
    const payload = jwt.verify(auth.slice(7), SECRET);
    req.user = payload;
    next();
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      return res.status(401).json({ error: 'Token expired' });
    }
    return res.status(401).json({ error: 'Invalid token' });
  }
}

Реализация (Python / FastAPI)

import jwt
from datetime import datetime, timedelta
from fastapi import Depends, HTTPException, Header

SECRET = os.environ["JWT_SECRET"]
ALGORITHM = "HS256"

def create_token(user_id: int, role: str) -> str:
    payload = {
        "user_id": user_id,
        "role": role,
        "exp": datetime.utcnow() + timedelta(hours=1),
        "iat": datetime.utcnow(),
    }
    return jwt.encode(payload, SECRET, algorithm=ALGORITHM)

async def jwt_auth(authorization: str = Header(...)):
    if not authorization.startswith("Bearer "):
        raise HTTPException(401, "Bearer token required")

    token = authorization[7:]
    try:
        payload = jwt.decode(token, SECRET, algorithms=[ALGORITHM])
        return payload
    except jwt.ExpiredSignatureError:
        raise HTTPException(401, "Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(401, "Invalid token")

@app.get("/api/me")
async def get_me(user=Depends(jwt_auth)):
    return {"user_id": user["user_id"], "role": user["role"]}

⚠️ JWT: Refresh Token Pattern

Access Token — короткоживущий (15 мин — 1 час). Refresh Token — долгоживущий (30 дней), хранится безопасно, используется для получения нового Access Token. Это стандартная практика.

5. OAuth 2.0

Сложный Делегированный доступ

Как работает

OAuth 2.0 — это протокол делегированной авторизации. Позволяет приложению получить доступ к ресурсам пользователя без передачи его пароля.

Authorization Code Flow (самый распространённый)

Пользователь
нажимает
«Login with Google»
Редирект на
Google OAuth
Пользователь
разрешает доступ
Редирект назад
с code
Сервер меняет
code на token
# Шаг 1: Редирект пользователя на Google
# https://accounts.google.com/o/oauth2/v2/auth?
#   client_id=YOUR_CLIENT_ID&
#   redirect_uri=https://yourapp.com/callback&
#   response_type=code&
#   scope=openid email profile&
#   state=random_csrf_token

# Шаг 2: Google редиректит назад с code
# https://yourapp.com/callback?code=AUTH_CODE&state=random_csrf_token

# Шаг 3: Сервер обменивает code на token
curl -X POST https://oauth2.googleapis.com/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "code=AUTH_CODE" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "redirect_uri=https://yourapp.com/callback" \
  -d "grant_type=authorization_code"

# Ответ:
# {"access_token": "ya29.xxx", "token_type": "Bearer", "expires_in": 3600}
Безопасность:
Очень высокая

Grant Types (потоки авторизации)

Grant TypeСценарийБезопасность
Authorization Code Веб-приложения с бэкендом Высокая
Authorization Code + PKCE SPA, мобильные приложения Высокая
Client Credentials Server-to-server (M2M) Высокая
Device Code Smart TV, IoT Средняя
Implicit SPA (устаревший) Низкая (deprecated)

Плюсы

  • Делегированный доступ (без передачи пароля)
  • Scopes — гранулярные разрешения
  • Стандарт индустрии
  • Поддержка SSO

Минусы

  • Сложная реализация
  • Много разных Grant Types
  • Требует правильной настройки
  • Overkill для простых API

Кто использует: Google, Facebook, GitHub, Twitter, Microsoft — все крупные платформы

6. HMAC (подпись запроса)

Сложный Максимальная защита

Как работает

Клиент подписывает каждый запрос секретным ключом. Сервер пересчитывает подпись и сравнивает. Защищает от подделки и replay-атак.

const crypto = require('crypto');

function signRequest(method, path, body, secret) {
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const message = `${timestamp}.${method}.${path}.${body || ''}`;
  const signature = crypto
    .createHmac('sha256', secret)
    .update(message)
    .digest('hex');

  return { signature, timestamp };
}

// Отправка
const { signature, timestamp } = signRequest(
  'POST', '/api/payments', '{"amount":100}', SECRET_KEY
);

// curl -X POST https://api.example.com/api/payments \
//   -H "X-Signature: sha256=abc123..." \
//   -H "X-Timestamp: 1708600000" \
//   -H "Content-Type: application/json" \
//   -d '{"amount":100}'
# Проверка подписи (сервер)
import hmac
import hashlib
import time

def verify_hmac(request):
    signature = request.headers.get('X-Signature', '').replace('sha256=', '')
    timestamp = request.headers.get('X-Timestamp', '')

    # Защита от replay-атак: запрос не старше 5 минут
    if abs(time.time() - int(timestamp)) > 300:
        return False

    message = f"{timestamp}.{request.method}.{request.path}.{request.body}"
    expected = hmac.new(
        SECRET_KEY.encode(), message.encode(), hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, expected)
Безопасность:
Очень высокая

Кто использует: AWS (Signature V4), Stripe (webhook signatures), Twilio, GitHub Webhooks

Сравнительная таблица

Метод Сложность Безопасность Stateless Лучший сценарий
API Key Простой Средняя Да Публичные API, server-to-server
Basic Auth Простой Низкая Да Внутренние сервисы, CI/CD
Bearer Token Средний Высокая Зависит Универсальный формат
JWT Средний Высокая Да SPA, мобильные, микросервисы
OAuth 2.0 Сложный Очень высокая Зависит Login with ..., делегированный доступ
HMAC Сложный Очень высокая Да Платёжные API, вебхуки

Как выбрать

Дерево решений

Best Practices

1. Всегда HTTPS

Без HTTPS любой метод аутентификации уязвим — токены, ключи и пароли передаются в открытом виде. Исключений нет.

2. Храните секреты правильно

3. Ротация и отзыв

4. Rate Limiting по ключу/токену

Ограничивайте количество запросов на API Key / пользователя. Возвращайте 429 Too Many Requests с заголовками X-RateLimit-*.

5. Логирование

Логируйте: кто, когда, откуда. Не логируйте: сами токены и пароли. Полезно для отладки и аудита безопасности.

FAQ

❓ Чем отличается аутентификация от авторизации?

Ответ: Аутентификация — «кто вы?» (токен, ключ, пароль). Авторизация — «что вам можно?» (роли, scopes). Сначала аутентификация, затем авторизация. API Key — только аутентификация. OAuth 2.0 — и то, и другое (через scopes).

❓ Какой метод аутентификации API выбрать?

Ответ: API Key — для серверных интеграций. JWT — для SPA/мобильных. OAuth 2.0 — для «Login with ...». Basic Auth — только внутренние сервисы через HTTPS. HMAC — для вебхуков и платёжных API.

❓ Безопасно ли передавать API Key в URL?

Ответ: Нет. URL попадает в логи, историю браузера, Referer. Передавайте API Key через заголовок X-API-Key или Authorization.

❓ Чем JWT лучше сессий для API?

Ответ: JWT — stateless (не нужно общее хранилище сессий), масштабируемость, кросс-доменная работа. Минусы: нельзя отозвать до истечения, больший размер. Сессии лучше для монолитов с мгновенным отзывом доступа.

Заключение

📝 Ключевые выводы

  1. API Key — просто, хорошо для серверных интеграций. Храните в заголовке, не в URL.
  2. JWT — stateless, идеален для SPA и микросервисов. Короткий exp + Refresh Token.
  3. OAuth 2.0 — стандарт для делегированного доступа. Используйте Authorization Code + PKCE.
  4. HMAC — максимальная защита для платёжных API и вебхуков.
  5. Basic Auth — только для внутренних сервисов через HTTPS.
  6. HTTPS обязателен для всех методов без исключений.

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

🎯 Тестируйте аутентификацию API

LightBox API — создавайте моковые эндпоинты с любым типом аутентификации для тестирования фронтенда и интеграций.

Попробовать бесплатно →

Статья опубликована: 23 февраля 2026
Автор: LightBox API Team