Безопасная работа с токенами

Токены в веб-приложениях используются для аутентификации и авторизации пользователей. Их неправильное хранение или передача может привести к компрометации безопасности приложения. На практике токены чаще всего представляют собой JSON Web Tokens (JWT), но подходы к безопасности применимы к любым типам токенов.

Ключевые принципы безопасной работы с токенами:

  1. Минимизация времени жизни токена Токены с коротким временем жизни снижают риск злоупотребления в случае их компрометации. Для долгосрочной аутентификации рекомендуется использовать refresh-токены, которые хранятся отдельно и используются только для получения нового access-токена.

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

    • HTTP-only cookies — предпочтительный способ хранения токена в браузере. Они недоступны из JavaScript, что защищает от XSS-атак.
    • LocalStorage / SessionStorage — удобны, но уязвимы к XSS. Использовать можно только в сочетании с дополнительными мерами защиты.
    • In-memory storage — хранение токена в оперативной памяти приложения минимизирует риск его утечки, однако требует повторной аутентификации после перезагрузки страницы.
  3. Передача токена через HTTPS Токены должны передаваться только по HTTPS, чтобы предотвратить их перехват в процессе передачи. Любая отправка по HTTP открывает путь для атак типа Man-in-the-Middle.

  4. Использование CSRF-защиты При хранении токена в cookie важно включать SameSite и CSRF-токены для защиты от межсайтовой подделки запросов.

Работа с токенами в Riot.js

Riot.js не предоставляет встроенного механизма управления токенами, но позволяет удобно интегрировать сторонние решения и контролировать жизненный цикл токена через компоненты и состояния приложения.

Структура хранения и передачи токенов в Riot.js может выглядеть так:

import { observable } from 'riot';

const authStore = observable({
  token: null,
  setToken(newToken) {
    this.token = newToken;
  },
  clearToken() {
    this.token = null;
  }
});
  • observable используется для реактивного обновления компонентов при изменении токена.
  • Методы setToken и clearToken позволяют централизованно управлять состоянием аутентификации.

Пример безопасного запроса с токеном:

async function fetchData() {
  const response = await fetch('/api/data', {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${authStore.token}`
    },
    credentials: 'include' // если используется cookie
  });
  return response.json();
}
  • Заголовок Authorization передает токен на сервер.
  • Опция credentials: 'include' необходима для отправки cookie при кросс-доменных запросах.

Обновление токенов

При использовании refresh-токенов логика обновления токена может быть встроена в хранилище состояния:

async function refreshToken() {
  const response = await fetch('/api/refresh', {
    method: 'POST',
    credentials: 'include'
  });
  const data = await response.json();
  authStore.setToken(data.accessToken);
}
  • Refresh-токен хранится только в http-only cookie.
  • Access-токен обновляется в оперативной памяти или в реактивном состоянии приложения.

Защита от атак

XSS (Cross-Site Scripting):

  • Не вставлять токены напрямую в HTML.
  • Использовать шаблонизатор Riot.js с безопасным связыванием данных.

CSRF (Cross-Site Request Forgery):

  • Хранение токена в cookie требует проверки CSRF-токена на сервере.
  • Настройка SameSite атрибута cookie ограничивает его отправку сторонними сайтами.

Защита от утечки через сторонние скрипты:

  • Минимизировать использование сторонних библиотек с доступом к document.cookie или localStorage.
  • Разделять зоны ответственности: токен используется только для авторизации API-запросов.

Итоговая архитектура безопасной работы с токенами

  1. Хранение access-токена в оперативной памяти или реактивном состоянии Riot.js.
  2. Хранение refresh-токена в http-only cookie.
  3. Передача токена только через HTTPS с заголовком Authorization.
  4. Контроль обновления токена через централизованное хранилище состояния.
  5. Реализация CSRF-защиты и минимизация XSS-рисков.

Такой подход обеспечивает высокий уровень безопасности и позволяет Riot.js-приложению управлять сессиями пользователей без уязвимостей, связанных с неправильной обработкой токенов.