Защита от CSRF
2.5.x ✅ 4.0.x ✅
Атаки подделки межсайтовых запросов (CSRF) заставляют пользователей выполнять нежелательные действия на сайте, где они аутентифицированы. XOOPS предоставляет встроенную защиту от CSRF через класс XoopsSecurity.
Связанная документация
Заголовок раздела «Связанная документация»- Security-Best-Practices - Комплексное руководство по безопасности
- Input-Sanitization - MyTextSanitizer и валидация
- SQL-Injection-Prevention - Практики безопасности базы данных
Понимание атак CSRF
Заголовок раздела «Понимание атак CSRF»Атака CSRF происходит, когда:
- Пользователь аутентифицирован на вашем сайте XOOPS
- Пользователь посещает вредоносный веб-сайт
- Вредоносный сайт отправляет запрос на ваш сайт XOOPS, используя сеанс пользователя
- Ваш сайт обрабатывает запрос так, как если бы он пришёл от законного пользователя
Класс XoopsSecurity
Заголовок раздела «Класс XoopsSecurity»XOOPS предоставляет класс XoopsSecurity для защиты от атак CSRF. Этот класс управляет токенами безопасности, которые должны быть включены в формы и проверены при обработке запросов.
Генерация токена
Заголовок раздела «Генерация токена»Класс безопасности генерирует уникальные токены, которые хранятся в сеансе пользователя и должны быть включены в формы:
$security = new XoopsSecurity();
// Получение поля HTML с входящим токеном$tokenHTML = $security->getTokenHTML();
// Получение только значения токена$tokenValue = $security->createToken();Проверка токена
Заголовок раздела «Проверка токена»При обработке отправки формы убедитесь, что токен действителен:
$security = new XoopsSecurity();
if (!$security->check()) { redirect_header('index.php', 3, _MD_TOKENEXPIRED); exit();}Использование системы токенов XOOPS
Заголовок раздела «Использование системы токенов XOOPS»С классами XoopsForm
Заголовок раздела «С классами XoopsForm»При использовании классов форм XOOPS защита токеном проста:
// Создание формы$form = new XoopsThemeForm('Add Item', 'form_name', 'submit.php');
// Добавление элементов формы$form->addElement(new XoopsFormText('Title', 'title', 50, 255, ''));$form->addElement(new XoopsFormTextArea('Content', 'content', ''));
// Добавление скрытого поля токена - ВСЕГДА включайте это$form->addElement(new XoopsFormHiddenToken());
// Добавление кнопки отправки$form->addElement(new XoopsFormButton('', 'submit', _SUBMIT, 'submit'));С пользовательскими формами
Заголовок раздела «С пользовательскими формами»Для пользовательских HTML-форм, которые не используют XoopsForm:
// В вашем шаблоне формы или PHP-файле$security = new XoopsSecurity();?><form method="post" action="submit.php"> <input type="text" name="title" /> <textarea name="content"></textarea>
<!-- Включение токена --> <?php echo $security->getTokenHTML(); ?>
<button type="submit">Submit</button></form>В шаблонах Smarty
Заголовок раздела «В шаблонах Smarty»При генерации форм в шаблонах Smarty:
// В вашем PHP-файле$security = new XoopsSecurity();$GLOBALS['xoopsTpl']->assign('token', $security->getTokenHTML());{* В вашем шаблоне *}<form method="post" action="submit.php"> <input type="text" name="title" /> <textarea name="content"></textarea>
{* Включение токена *} <{$token}>
<button type="submit">Submit</button></form>Обработка отправки форм
Заголовок раздела «Обработка отправки форм»Базовая проверка токена
Заголовок раздела «Базовая проверка токена»// В вашем скрипте обработки формы$security = new XoopsSecurity();
// Проверка токенаif (!$security->check()) { redirect_header('index.php', 3, _MD_TOKENEXPIRED); exit();}
// Токен действителен, обработка формы$title = $_POST['title'];// ... продолжение обработкиС пользовательской обработкой ошибок
Заголовок раздела «С пользовательской обработкой ошибок»$security = new XoopsSecurity();
if (!$security->check()) { // Получение подробной информации об ошибке $errors = $security->getErrors();
// Логирование ошибки error_log('CSRF token validation failed: ' . implode(', ', $errors));
// Перенаправление с сообщением об ошибке redirect_header('form.php', 3, 'Security token expired. Please try again.'); exit();}Для запросов AJAX
Заголовок раздела «Для запросов AJAX»При работе с запросами AJAX включите токен в ваш запрос:
// JavaScript - получение токена из скрытого поляvar token = document.querySelector('input[name="XOOPS_TOKEN_REQUEST"]').value;
// Включение в запрос AJAXfetch('ajax_handler.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'action=save&XOOPS_TOKEN_REQUEST=' + encodeURIComponent(token)});// Обработчик PHP AJAX$security = new XoopsSecurity();
if (!$security->check()) { echo json_encode(['error' => 'Invalid security token']); exit();}
// Обработка запроса AJAXПроверка HTTP Referer
Заголовок раздела «Проверка HTTP Referer»Для дополнительной защиты, особенно для запросов AJAX, вы также можете проверить заголовок HTTP referer:
$security = new XoopsSecurity();
// Проверка заголовка refererif (!$security->checkReferer()) { echo json_encode(['error' => 'Invalid request']); exit();}
// Также проверка токенаif (!$security->check()) { echo json_encode(['error' => 'Invalid token']); exit();}Комбинированная проверка безопасности
Заголовок раздела «Комбинированная проверка безопасности»$security = new XoopsSecurity();
// Выполнение обеих проверокif (!$security->checkReferer() || !$security->check()) { redirect_header('index.php', 3, 'Security validation failed'); exit();}Конфигурация токена
Заголовок раздела «Конфигурация токена»Время жизни токена
Заголовок раздела «Время жизни токена»Токены имеют ограниченное время жизни, чтобы предотвратить атаки повтора. Вы можете настроить это в параметрах XOOPS или обработать истёкшие токены корректно:
$security = new XoopsSecurity();
if (!$security->check()) { // Токен может истечь // Регенерировать форму с новым токеном redirect_header('form.php', 3, 'Your session has expired. Please submit the form again.'); exit();}Несколько форм на одной странице
Заголовок раздела «Несколько форм на одной странице»Если на странице несколько форм, каждая должна иметь свой токен:
// Форма 1$form1 = new XoopsThemeForm('Form 1', 'form1', 'submit1.php');$form1->addElement(new XoopsFormHiddenToken('token1'));
// Форма 2$form2 = new XoopsThemeForm('Form 2', 'form2', 'submit2.php');$form2->addElement(new XoopsFormHiddenToken('token2'));Лучшие практики
Заголовок раздела «Лучшие практики»Всегда используйте токены для операций, изменяющих состояние
Заголовок раздела «Всегда используйте токены для операций, изменяющих состояние»Включайте токены в любую форму, которая:
- Создаёт данные
- Обновляет данные
- Удаляет данные
- Изменяет параметры пользователя
- Выполняет любое административное действие
Не полагайтесь исключительно на проверку Referer
Заголовок раздела «Не полагайтесь исключительно на проверку Referer»Заголовок HTTP referer может быть:
- Удалён инструментами конфиденциальности
- Отсутствовать в некоторых браузерах
- Подделан в некоторых случаях
Всегда используйте проверку токена в качестве основной защиты.
Регенерируйте токены правильно
Заголовок раздела «Регенерируйте токены правильно»Рассмотрите регенерацию токенов:
- После успешной отправки формы
- После входа/выхода
- Через регулярные промежутки времени для продолжительных сеансов
Обработка истечения токена корректно
Заголовок раздела «Обработка истечения токена корректно»$security = new XoopsSecurity();
if (!$security->check()) { // Сохранение данных формы временно $_SESSION['form_backup'] = $_POST;
// Перенаправление обратно на форму с сообщением redirect_header('form.php?restore=1', 3, 'Please resubmit the form.'); exit();}Распространённые проблемы и решения
Заголовок раздела «Распространённые проблемы и решения»Ошибка “Токен не найден”
Заголовок раздела «Ошибка “Токен не найден”»Проблема: Проверка безопасности не удаётся с ошибкой “token not found”
Решение: Убедитесь, что поле токена включено в вашу форму:
$form->addElement(new XoopsFormHiddenToken());Ошибка истечения токена
Заголовок раздела «Ошибка истечения токена»Проблема: Пользователи видят “token expired” после длительного заполнения формы
Решение: Рассмотрите использование JavaScript для периодического обновления токена:
// Обновление токена каждые 10 минутsetInterval(function() { fetch('refresh_token.php') .then(response => response.json()) .then(data => { document.querySelector('input[name="XOOPS_TOKEN_REQUEST"]').value = data.token; });}, 600000);Проблемы с токеном AJAX
Заголовок раздела «Проблемы с токеном AJAX»Проблема: Запросы AJAX не проходят проверку токена
Решение: Убедитесь, что токен передаётся с каждым запросом AJAX и проверяется на стороне сервера:
// Обработчик AJAXheader('Content-Type: application/json');
$security = new XoopsSecurity();if (!$security->check(true, false)) { // Не очищайте токен для AJAX http_response_code(403); echo json_encode(['error' => 'Invalid token']); exit();}Пример: полная реализация формы
Заголовок раздела «Пример: полная реализация формы»<?phprequire_once dirname(__DIR__) . '/mainfile.php';
$security = new XoopsSecurity();
// Обработка отправки формыif ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!$security->check()) { redirect_header('form.php', 3, 'Security token expired. Please try again.'); exit(); }
// Обработка корректной отправки $title = $myts->htmlSpecialChars($_POST['title']); // ... сохранение в базе данных
redirect_header('success.php', 3, 'Item saved successfully!'); exit();}
// Отображение формы$GLOBALS['xoopsOption']['template_main'] = 'mymodule_form.tpl';include XOOPS_ROOT_PATH . '/header.php';
$form = new XoopsThemeForm('Add Item', 'add_item', 'form.php');$form->addElement(new XoopsFormText('Title', 'title', 50, 255, ''));$form->addElement(new XoopsFormTextArea('Content', 'content', ''));$form->addElement(new XoopsFormHiddenToken());$form->addElement(new XoopsFormButton('', 'submit', _SUBMIT, 'submit'));
$GLOBALS['xoopsTpl']->assign('form', $form->render());
include XOOPS_ROOT_PATH . '/footer.php';#security #csrf #xoops #forms #tokens #XoopsSecurity