CSRF атаки и токены

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

Механизм CSRF атак

CSRF атакующий использует существующую аутентификацию пользователя. Основные шаги атаки:

  1. Пользователь входит на защищённый сайт, сервер создает сессию и устанавливает cookie.
  2. Пользователь посещает сторонний сайт, где злоумышленник размещает форму или скрипт, отправляющий запрос на защищённый сайт.
  3. Браузер автоматически прикрепляет cookie сессии к запросу.
  4. Сервер обрабатывает запрос как легитимный, выполняя действие от имени пользователя.

Пример типичного сценария: форма для изменения пароля или перевода средств, которая может быть вызвана с другого сайта через POST-запрос.

Методы защиты

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

Принцип работы токенов:

  • Сервер генерирует токен и включает его в HTML-форму или в заголовок ответа.
  • Клиент (в нашем случае Vue.js приложение) вставляет токен в каждый защищённый запрос.
  • Сервер проверяет наличие и корректность токена при обработке запроса.
  • Запрос без правильного токена отклоняется.

Интеграция CSRF токенов в Vue.js

  1. Получение токена

Если токен передаётся через метатег в HTML:

<meta name="csrf-token" content="UNIQUE_CSRF_TOKEN">

В Vue.js его можно получить через Jav * aScript:

const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
  1. Добавление токена к HTTP-запросам

При использовании axios токен можно передавать через заголовок X-CSRF-Token:

import axios from 'axios';

axios.defaults.headers.common['X-CSRF-Token'] = csrfToken;

axios.post('/api/update-profile', {
  name: 'Иван'
}).then(response => {
  console.log('Данные обновлены', response.data);
}).catch(error => {
  console.error('Ошибка обновления', error);
});

Альтернатива — добавление токена в тело POST-запроса, если сервер ожидает его в параметрах формы:

axios.post('/api/update-profile', {
  name: 'Иван',
  _csrf: csrfToken
});
  1. Автоматизация в глобальной конфигурации Vue

Для проектов на Vue 3 с Composition API и axios удобно создать плагин, который автоматически прикрепляет токен ко всем запросам:

import { createApp } from 'vue';
import axios from 'axios';
import App from './App.vue';

const app = createApp(App);

axios.interceptors.request.use(config => {
  const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
  if (token) {
    config.headers['X-CSRF-Token'] = token;
  }
  return config;
});

app.config.globalProperties.$http = axios;
app.mount('#app');

Это позволяет не вставлять токен вручную в каждый компонент.

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

  • Токен должен быть уникальным для каждой сессии или запроса и храниться в безопасном месте на сервере.
  • Использовать SameSite cookie, чтобы ограничить отправку cookie только с доверенного домена.
  • Проверять токен на стороне сервера перед выполнением критических действий.
  • Для REST API, особенно если используется JWT аутентификация, рекомендуется использовать заголовки авторизации вместо cookie, что снижает риск CSRF, но токены остаются актуальным инструментом защиты для форм и сессий.

Особенности при работе с SPA на Vue.js

Vue.js как фронтенд-фреймворк активно использует AJAX-запросы, поэтому CSRF токены часто передаются через заголовки. Важно:

  • Всегда обновлять токен после изменения сессии.
  • Проверять корректность токена на сервере, чтобы предотвращать атаки при длительных сессиях.
  • Использовать централизованное управление HTTP-запросами (axios interceptors или Vue plugins), чтобы исключить ошибки при ручной вставке токена.

Примеры серверной проверки

На Node.js с использованием Express и middleware csurf проверка выглядит так:

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

const app = express();
app.use(cookieParser());
app.use(csurf({ cookie: true }));

app.get('/form', (req, res) => {
  res.send(`<form method="POST" action="/update">
    <input type="hidden" name="_csrf" value="${req.csrfToken()}">
    <input type="text" name="name">
    <button type="submit">Отправить</button>
  </form>`);
});

app.post('/update', (req, res) => {
  res.send('Данные обновлены');
});

Совместное использование CSRF токенов и корректной настройки заголовков безопасности позволяет снизить риск атак практически до нуля, даже при работе с современными SPA на Vue.js.