← Назад к вопросам

Зачем хранить JWT в Cookie?

1.8 Middle🔥 191 комментариев
#REST API и HTTP#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Зачем Хранить JWT в Cookie

Хранение JWT (JSON Web Token) в cookie — это практика безопасности, которая имеет как преимущества, так и недостатки. Это альтернатива хранению JWT в localStorage или sessionStorage браузера.

Что Такое JWT

JWT — это компактный, URL-безопасный способ передачи информации между клиентом и сервером в виде JSON объекта. JWT состоит из трех частей, разделенных точками:

header.payload.signature

Пример:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Места Хранения JWT

1. localStorage

// Сохранение
localStorage.setItem('token', jwtToken);

// Использование в запросах
const headers = {
    'Authorization': `Bearer ${localStorage.getItem('token')}`
};
fetch('/api/data', { headers });

Проблемы:

  • Уязвим к XSS (Cross-Site Scripting) атакам
  • JavaScript код может украсть токен
  • Нет защиты от CSRF

2. sessionStorage

sessionStorage.setItem('token', jwtToken);

Проблемы:

  • Те же XSS уязвимости
  • Очищается при закрытии браузера

3. Cookie (более безопасно)

# На backend (Python/FastAPI)
from fastapi.responses import JSONResponse

@app.post("/login")
async def login(credentials):
    user = authenticate_user(credentials)
    token = create_jwt_token(user)
    
    response = JSONResponse({"status": "success"})
    response.set_cookie(
        key="access_token",
        value=token,
        httponly=True,  # защита от XSS
        secure=True,    # только HTTPS
        samesite="Strict",  # защита от CSRF
        max_age=3600
    )
    return response

Преимущества Хранения JWT в Cookie

1. Защита от XSS (Cross-Site Scripting)

# httponly=True предотвращает доступ из JavaScript
response.set_cookie(
    key="access_token",
    value=token,
    httponly=True  # JavaScript НЕ может получить доступ
)

JavaScript не может украсть токен:

// Это НЕ сработает
const token = document.cookie;  // пустая строка для httponly cookies
console.log(localStorage.getItem('token'));  // токен украден!

2. Автоматическая Отправка с Каждым Запросом

Browser автоматически отправляет cookie с каждым запросом к серверу:

# На backend
@app.get("/api/user")
async def get_user(request: Request):
    token = request.cookies.get("access_token")
    user = verify_token(token)
    return user
// На frontend — не нужно добавлять токен вручную
fetch('/api/user', {
    method: 'GET',
    credentials: 'include'  // отправить cookies
});
// Браузер автоматически отправит cookie

3. Защита от CSRF (Cross-Site Request Forgery)

С правильными настройками cookie:

response.set_cookie(
    key="access_token",
    value=token,
    httponly=True,
    secure=True,
    samesite="Strict"  # защита от CSRF
)

Как это работает:

  • samesite="Strict" — cookie отправляется только запросам с того же сайта
  • Предотвращает отправку cookie с cross-site запросов

4. Стандартный HTTP Протокол

Cookie — это часть HTTP протокола, поддерживаемая всеми браузерами:

HTTP/1.1 200 OK
Set-Cookie: access_token=eyJ...; HttpOnly; Secure; SameSite=Strict
Content-Type: application/json

{"status": "logged in"}

Недостатки Хранения JWT в Cookie

1. Сложнее Работать с Multiple Domains

Cookie привязаны к domain:

# Проблема: API на другом домене
# Frontend: example.com
# API: api.example.com

# Решение: использовать CORS с правильными настройками
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://example.com"],
    allow_credentials=True,  # разрешить отправку cookies
)

2. Проблемы с Mobile Apps

Native мобильные приложения не имеют встроенной поддержки cookie:

// iOS — нужно обрабатывать cookies вручную
let config = URLSessionConfiguration.default
config.httpShouldSetCookies = true
let session = URLSession(configuration: config)

// Или использовать localStorage для мобильных
UserDefaults.standard.set(token, forKey: "access_token")

3. Logout Сложнее

Делать logout с cookie требует участия сервера:

@app.post("/logout")
async def logout(response: Response):
    response.delete_cookie("access_token")
    return {"status": "logged out"}

Полный Пример: Безопасная Аутентификация

Backend (Python/FastAPI)

from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
from datetime import datetime, timedelta
import jwt
from passlib.context import CryptContext

app = FastAPI()
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
pwd_context = CryptContext(schemes=["bcrypt"])

@app.post("/login")
async def login(credentials: dict):
    user = authenticate_user(credentials["email"], credentials["password"])
    
    if not user:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    
    # Создать JWT token
    payload = {
        "sub": user["id"],
        "email": user["email"],
        "exp": datetime.utcnow() + timedelta(hours=1)
    }
    token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
    
    # Сохранить в cookie
    response = JSONResponse({"status": "success"})
    response.set_cookie(
        key="access_token",
        value=token,
        httponly=True,     # защита от XSS
        secure=True,       # только HTTPS
        samesite="Strict", # защита от CSRF
        max_age=3600       # 1 час
    )
    return response

@app.get("/api/user")
async def get_user(request: Request):
    token = request.cookies.get("access_token")
    
    if not token:
        raise HTTPException(status_code=401, detail="Not authenticated")
    
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        user_id = payload.get("sub")
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="Token expired")
    except jwt.InvalidTokenError:
        raise HTTPException(status_code=401, detail="Invalid token")
    
    user = get_user_by_id(user_id)
    return user

@app.post("/logout")
async def logout(response: Response):
    response.delete_cookie("access_token")
    return {"status": "logged out"}

Frontend (JavaScript)

// Логин
async function login(email, password) {
    const response = await fetch('https://api.example.com/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',  // отправить cookies
        body: JSON.stringify({ email, password })
    });
    
    if (response.ok) {
        console.log('Logged in!');
        // Токен в cookie, доступен только серверу
    }
}

// Получить данные пользователя
async function getUser() {
    const response = await fetch('https://api.example.com/api/user', {
        method: 'GET',
        credentials: 'include'  // браузер отправит cookie
    });
    
    const user = await response.json();
    return user;
}

// Logout
async function logout() {
    const response = await fetch('https://api.example.com/logout', {
        method: 'POST',
        credentials: 'include'
    });
    
    if (response.ok) {
        console.log('Logged out!');
        // Cookie удалена
    }
}

Сравнение Подходов

АспектlocalStorageCookie (httponly)
XSS защитаНетДа
CSRF защитаНетДа (с samesite)
Автоматическая отправкаНетДа
Доступ из JavaScriptДа (опасно)Нет (безопасно)
Multi-domainПрощеСложнее
Mobile appsПрощеСложнее
LogoutКлиентСервер

Лучшие Практики

  1. Всегда используй HTTPS для защиты от перехвата
  2. Установи httponly = True для защиты от XSS
  3. Установи secure = True для HTTPS-only
  4. Установи samesite = "Strict" для защиты от CSRF
  5. Установи short expiration (1-15 минут для access токена)
  6. Используй refresh токены для долгосрочной аутентификации
  7. Регулярно ротируй ключи подписи токенов

Заключение

Хранение JWT в cookie — это более безопасный подход чем localStorage, благодаря защите от XSS и CSRF атак. Cookie автоматически отправляются с каждым запросом и недоступны для JavaScript кода. Однако этот подход требует правильной конфигурации CORS и может быть сложнее для мобильных приложений. Выбирайте метод в зависимости от архитектуры вашего приложения и требований безопасности.

Зачем хранить JWT в Cookie? | PrepBro