在ProcessWire中让Ajax请求支持CSRF保护的方法

这篇文章写过CSRF的防护方法,但是在ajax请求中用ajax动态引入csrfToken到表单中是完全不行的,这时候我们需要通过headers发送认证的特殊处理

首先在文件/wire/core/SessionCSRF.php中有这么一段关于Ajax请求验证的实现代码:

		if($config->ajax && isset($_SERVER[$headerName]) && $_SERVER[$headerName] === $tokenValue) {
			$valid = true;
		} else if($input->post($tokenName) === $tokenValue) {
			$valid = true; 
		}

实现过程

准备前端Token

首先我们需要生成Token,如果是动态页面只需要拿到token

$csrfTokenName = $session->CSRF->getTokenName();
$csrfTokenValue = $session->CSRF->getTokenValue();

在页面中加入这行

<div class="js-csrf-token" style="display:none;" data-csrf-token-name="<?php echo $csrfTokenName; ?>" data-csrf-token-value="<?php echo $csrfTokenValue; ?>"></div>

如果像我一样用了phpFastCache生成出纯静态文件,绕过了ProcessWire系统,那么需要服务端生成出Token然后通过Ajax引入Token数据并在前端生成出上面的代码。

最后在前端JS文件中拿数据,我们用这个函数来实现

function getCsrfToken() {
	const csrfTokenNode = document.getElementsByClassName('js-csrf-token')[0];

	return {
		name: csrfTokenNode.getAttribute('data-csrf-token-name'),
		value: csrfTokenNode.getAttribute('data-csrf-token-value'),
	}
}

这是第一步,也是最重要的一步。

构建带有Headers的Ajax验证请求

构建headers

const csrfToken = getCsrfToken();
let xhr = new XMLHttpRequest();

// ...
// open the request and do some configuration
// ...

xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); // tell the server and PW that it's an AJAX request
xhr.setRequestHeader('X-' + csrfToken.name, csrfToken.value); // create a header with the token

需要注意的是,如果使用jQuery,添加headers参数即可。

$.ajax({
    url: "/test",
    headers: {"X-Test-Header": "test-value"}
});

后端请求验证

最后到服务端来接收Token验证

if ($session->CSRF->hasValidToken()) {
	// works for AJAX!
}

如果要自定义token名,可以这么处理

$csrfTokenName = $session->CSRF->getTokenName('myAjaxToken'); // this creates a token if one with the given id doesn't exist
$csrfTokenValue = $session->CSRF->getTokenValue('myAjaxToken');

验证

f ($session->CSRF->hasValidToken('myAjaxToken') {
// ...
}

Post Comment