跳转到内容

“CSRF保护”

2.5.x ✅ 4.0.x ✅

交叉-Site请求伪造(CSRF)攻击诱骗用户在经过身份验证的网站上执行不需要的操作。 XOOPS 通过XOOPSSecurity 类提供内置-in CSRF 保护。

  • 安全-Best-Practices - 全面的安全指南
  • 输入-Sanitization - MyTextSanitizer 和验证
  • SQL-Injection-Prevention - 数据库安全实践

CSRF 攻击发生在以下情况:

  1. 用户在您的 XOOPS 网站上经过身份验证 2、用户访问恶意网站
  2. 恶意网站使用用户会话向您的XOOPS网站提交请求
  3. 您的网站处理该请求,就像它来自合法用户一样

XOOPS 提供 XOOPSSecurity 类来防止 CSRF 攻击。此类管理必须包含在表单中并在处理请求时进行验证的安全令牌。

安全类生成存储在用户会话中的唯一令牌,并且必须包含在表单中:

$security = new XoopsSecurity();
// Get token HTML input field
$tokenHTML = $security->getTokenHTML();
// Get just the token value
$tokenValue = $security->createToken();

处理表单提交时,验证令牌是否有效:

$security = new XoopsSecurity();
if (!$security->check()) {
redirect_header('index.php', 3, _MD_TOKENEXPIRED);
exit();
}

当使用XOOPS表单类时,令牌保护很简单:

// Create a form
$form = new XoopsThemeForm('Add Item', 'form_name', 'submit.php');
// Add form elements
$form->addElement(new XoopsFormText('Title', 'title', 50, 255, ''));
$form->addElement(new XoopsFormTextArea('Content', 'content', ''));
// Add hidden token field - ALWAYS include this
$form->addElement(new XoopsFormHiddenToken());
// Add submit button
$form->addElement(new XoopsFormButton('', 'submit', _SUBMIT, 'submit'));

对于不使用 XOOPSForm 的自定义 HTML 表单:

// In your form template or PHP file
$security = new XoopsSecurity();
?>
<form method="post" action="submit.php">
<input type="text" name="title" />
<textarea name="content"></textarea>
<!-- Include the token -->
<?php echo $security->getTokenHTML(); ?>
<button type="submit">Submit</button>
</form>

在 Smarty 模板中生成表单时:

// In your PHP file
$security = new XoopsSecurity();
$GLOBALS['xoopsTpl']->assign('token', $security->getTokenHTML());
{* In your template *}
<form method="post" action="submit.php">
<input type="text" name="title" />
<textarea name="content"></textarea>
{* Include the token *}
<{$token}>
<button type="submit">Submit</button>
</form>
// In your form processing script
$security = new XoopsSecurity();
// Verify the token
if (!$security->check()) {
redirect_header('index.php', 3, _MD_TOKENEXPIRED);
exit();
}
// Token is valid, process the form
$title = $_POST['title'];
// ... continue processing
$security = new XoopsSecurity();
if (!$security->check()) {
// Get detailed error information
$errors = $security->getErrors();
// Log the error
error_log('CSRF token validation failed: ' . implode(', ', $errors));
// Redirect with error message
redirect_header('form.php', 3, 'Security token expired. Please try again.');
exit();
}

处理 AJAX 请求时,请在请求中包含令牌:

// JavaScript - get token from hidden field
var token = document.querySelector('input[name="XOOPS_TOKEN_REQUEST"]').value;
// Include in AJAX request
fetch('ajax_handler.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'action=save&XOOPS_TOKEN_REQUEST=' + encodeURIComponent(token)
});
// PHP AJAX handler
$security = new XoopsSecurity();
if (!$security->check()) {
echo json_encode(['error' => 'Invalid security token']);
exit();
}
// Process AJAX request

如需额外保护,尤其是针对 AJAX 请求,您还可以检查 HTTP 引荐来源网址:

$security = new XoopsSecurity();
// Check referer header
if (!$security->checkReferer()) {
echo json_encode(['error' => 'Invalid request']);
exit();
}
// Also verify the token
if (!$security->check()) {
echo json_encode(['error' => 'Invalid token']);
exit();
}
$security = new XoopsSecurity();
// Perform both checks
if (!$security->checkReferer() || !$security->check()) {
redirect_header('index.php', 3, 'Security validation failed');
exit();
}

令牌的生命周期有限,以防止重放攻击。您可以在 XOOPS 设置中进行配置或优雅地处理过期的令牌:

$security = new XoopsSecurity();
if (!$security->check()) {
// Token may have expired
// Regenerate form with new token
redirect_header('form.php', 3, 'Your session has expired. Please submit the form again.');
exit();
}

当同一页面上有多个表单时,每个表单都应该有自己的令牌:

// Form 1
$form1 = new XoopsThemeForm('Form 1', 'form1', 'submit1.php');
$form1->addElement(new XoopsFormHiddenToken('token1'));
// Form 2
$form2 = new XoopsThemeForm('Form 2', 'form2', 'submit2.php');
$form2->addElement(new XoopsFormHiddenToken('token2'));

始终使用代币进行状态-Changing操作

Section titled “始终使用代币进行状态-Changing操作”

包括任何形式的令牌:

  • 创建数据
  • 更新数据
  • 删除数据
  • 更改用户设置
  • 执行任何行政行动

HTTP引用标头可以是:

  • 被隐私工具剥夺
  • 某些浏览器中缺失
  • 在某些情况下被欺骗

始终使用令牌验证作为主要防御措施。

考虑重新生成令牌:

  • 成功提交表单后
  • login/logout之后
  • 定期进行长时间训练
$security = new XoopsSecurity();
if (!$security->check()) {
// Store form data temporarily
$_SESSION['form_backup'] = $_POST;
// Redirect back to form with message
redirect_header('form.php?restore=1', 3, 'Please resubmit the form.');
exit();
}

问题:安全检查失败并显示“未找到令牌”

解决方案:确保令牌字段包含在您的表单中:

$form->addElement(new XoopsFormHiddenToken());

问题:用户在完成长表单后看到“令牌已过期”

解决方案:考虑使用JavaScript定期刷新令牌:

// Refresh token every 10 minutes
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 请求都传递令牌,并验证服务器-side:

// AJAX handler
header('Content-Type: application/json');
$security = new XoopsSecurity();
if (!$security->check(true, false)) { // Don't clear token for AJAX
http_response_code(403);
echo json_encode(['error' => 'Invalid token']);
exit();
}
form.php
<?php
require_once dirname(__DIR__) . '/mainfile.php';
$security = new XoopsSecurity();
// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!$security->check()) {
redirect_header('form.php', 3, 'Security token expired. Please try again.');
exit();
}
// Process valid submission
$title = $myts->htmlSpecialChars($_POST['title']);
// ... save to database
redirect_header('success.php', 3, 'Item saved successfully!');
exit();
}
// Display form
$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