CSRF и токены безопасности

Cross-Site Request Forgery (CSRF) — это тип уязвимости веб-приложений, при котором злоумышленник вынуждает пользователя выполнить нежелательные действия на сайте, где у него уже есть авторизация. Основная опасность CSRF заключается в том, что запросы выполняются с полномочиями легитимного пользователя, что позволяет изменять данные, совершать транзакции или менять настройки аккаунта без ведома пользователя.

Механизм CSRF

CSRF работает следующим образом:

  1. Пользователь авторизован на сайте example.com.
  2. Злоумышленник создает страницу или скрипт, который отправляет HTTP-запрос к example.com с использованием куки авторизации пользователя.
  3. Когда пользователь посещает страницу злоумышленника, запрос автоматически выполняется с его сессионными данными.

Типичный пример — форма отправки денег или изменение пароля, которую можно инициировать через скрытую форму на чужом сайте.

Роль токенов безопасности

Токены безопасности (CSRF-токены) используются для защиты от подобных атак. Они представляют собой уникальные случайные строки, которые:

  • Генерируются сервером при загрузке страницы или при начале сессии пользователя.
  • Встраиваются в HTML-формы или в заголовки AJAX-запросов.
  • Проверяются сервером при получении POST/PUT/DELETE-запросов.

Ключевые моменты работы токенов:

  • Токен должен быть непредсказуемым и уникальным для каждой сессии.
  • Он должен передаваться только в теле запроса (например, как скрытое поле формы) или в заголовке.
  • Сервер отвергает все запросы, в которых отсутствует корректный токен.

Интеграция CSRF-токенов в Preact

Preact, как лёгкая альтернатива React, часто используется с REST API или GraphQL на стороне клиента. Защита от CSRF при этом требует корректной работы с токенами на клиенте.

Добавление токена в формы:

import { h } from 'preact';
import { useState } from 'preact/hooks';

function SecureForm({ csrfToken }) {
  const [value, setValue] = useState('');

  const handleSubmit = async (event) => {
    event.preventDefault();
    const response = await fetch('/api/submit', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken
      },
      body: JSON.stringify({ value })
    });
    const result = await response.json();
    console.log(result);
  };

  return (
    <form onSub mit={handleSubmit}>
      <input
        type="text"
        value={value}
        onIn put={(e) => setValue(e.target.value)}
      />
      <button type="submit">Отправить</button>
    </form>
  );
}

В этом примере CSRF-токен передается через заголовок X-CSRF-Token, что является безопасным способом защиты AJAX-запросов.

Обработка токена на сервере:

На серверной стороне необходимо проверять токен перед выполнением действий, изменяющих состояние. Пример на Node.js с Express:

import express from 'express';
import csrf from 'csurf';
import cookieParser from 'cookie-parser';

const app = express();
app.use(cookieParser());
app.use(express.json());

const csrfProtection = csrf({ cookie: true });

app.get('/form', csrfProtection, (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

app.post('/api/submit', csrfProtection, (req, res) => {
  // Если токен корректен, выполняется действие
  res.json({ status: 'success', value: req.body.value });
});

Здесь middleware csrf проверяет наличие токена в заголовках или теле запроса, что предотвращает CSRF-атаки.

Практические рекомендации

  • Никогда не включать CSRF-токены в URL, так как они могут быть перехвачены в логах или реферерах.
  • Использовать отдельный токен для каждой сессии или формы, чтобы минимизировать риск повторного использования.
  • Для SPA на Preact оптимально хранить токен в памяти компонента или в контексте приложения, а не в локальном хранилище браузера.
  • Обновлять токен при каждом изменении сессии или авторизации, чтобы предотвратить использование старых токенов.

Эффективное использование CSRF-токенов в связке с Preact и современными API позволяет обеспечить высокий уровень безопасности пользовательских данных и предотвратить наиболее распространённые виды атак, основанных на подделке запросов.