Что такое CSRF-токен и как его реализовать?php-23

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

Как работает CSRF-токен?

  1. Сервер генерирует уникальный токен для сессии пользователя
  2. Токен добавляется в формы или AJAX-запросы
  3. При получении POST/PUT/DELETE запроса сервер проверяет токен
  4. Если токен не совпадает — запрос отклоняется

Базовая реализация CSRF-защиты

1. Генерация токена

// Создаем токен если его нет
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

2. Добавление в форму

<form action="/process" method="post">
    <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
    <!-- другие поля формы -->
    <button type="submit">Отправить</button>
</form>

3. Проверка токена

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
        http_response_code(403);
        die('Неверный CSRF-токен');
    }

    // Обработка формы
}

Улучшенная реализация

Класс для работы с CSRF

class CsrfGuard {
    public static function generateToken(): string {
        if (empty($_SESSION['csrf_token'])) {
            $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
        }
        return $_SESSION['csrf_token'];
    }

    public static function validateToken(string $token): bool {
        return isset($_SESSION['csrf_token']) &&
               hash_equals($_SESSION['csrf_token'], $token);
    }

    public static function getTokenField(): string {
        return '<input type="hidden" name="csrf_token" value="' . self::generateToken() . '">';
    }
}

Использование в middleware

function csrfMiddleware($request, $response, $next) {
    if ($request->isPost()) {
        if (!CsrfGuard::validateToken($request->get('csrf_token'))) {
            return $response->withStatus(403);
        }
    }

    return $next($request, $response);
}

Особенности реализации

  1. Используйте cryptographically secure генератор (random_bytes())
  2. Сравнивайте токены безопасно (hash_equals() вместо ==)
  3. Обновляйте токен после важных действий (смена пароля, платежи)
  4. Разные токены для разных форм (если нужно)

Интеграция с фронтендом

Для AJAX-запросов

// В шаблоне
<meta name="csrf-token" content="<?= $_SESSION['csrf_token'] ?>">

// В JavaScript
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

Проверка в PHP для AJAX

if ($_SERVER['HTTP_X_CSRF_TOKEN'] !== $_SESSION['csrf_token']) {
    http_response_code(403);
    die('Invalid CSRF token');
}

Дополнительные меры защиты

  1. SameSite cookies:

    session_set_cookie_params([
        'samesite' => 'Strict',
        'secure' => true,
        'httponly' => true
    ]);
    
  2. Проверка Origin/Referer заголовков (дополнительная защита):

    $allowedOrigins = ['https://yourdomain.com'];
    if (!in_array($_SERVER['HTTP_ORIGIN'], $allowedOrigins)) {
        die('Invalid request origin');
    }
    

Резюмируем:

CSRF-токен — это обязательная мера безопасности для любого веб-приложения. Реализуйте его с помощью генерации уникального токена на сессию, встраивания в формы и строгой проверки на сервере. Для максимальной защиты комбинируйте CSRF-токены с SameSite cookies и проверкой Origin заголовков.