cakephp3.7 测试中的 CSRF 令牌不匹配
CSRF token mismatch in cakephp3.7 tests
tests/TestCase/Controller/FeedbackControllerTest.php:45
public function testAdd()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->session([
'Auth' => [
'User' => [
'id' => 1,
'role' => 'REPR',
]
]
]);
$this->configRequest([
'headers' => ['Accept' => 'application/json']
]);
$_data = [
'crash' => 1,
'details' => 'Lorem ipsum dolor sit amet'
];
$_data = json_encode($_data, JSON_PRETTY_PRINT);
$this->post('/feedback/add', $_data); // <---- 45
$expected = [
'status' => 'success'
];
$expected = json_encode($expected, JSON_PRETTY_PRINT);
$this->assertEquals($expected, (string)$this->_response->getBody());
}
PHP单元输出:
1) App\Test\TestCase\Controller\FeedbackControllerTest::testAdd
Cake\Http\Exception\InvalidCsrfTokenException: Missing CSRF token cookie
/vagrant/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:196
/vagrant/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:120
/vagrant/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:106
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:51
/vagrant/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php:168
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php:88
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php:96
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:51
/vagrant/vendor/cakephp/cakephp/src/Http/Server.php:98
/vagrant/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php:201
/vagrant/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php:516
/vagrant/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php:413
/vagrant/tests/TestCase/Controller/FeedbackControllerTest.php:45
我已经阅读并尝试了答案中的解决方案:
如果我像@ndm 说的那样添加:
$token = 'my-csrf-token';
$this->cookie('csrfToken', $token);
$data = [
'email' => 'info@example.com',
'password' => 'secret',
'_csrfToken' => $token
];
然后:
Cake\Http\Exception\InvalidCsrfTokenException: CSRF 令牌不匹配。
如何修复?
能否在 jquery ajax 函数中尝试这段代码,
beforeSend: function (xhr)
{
xhr.setRequestHeader('X-CSRF-Token', $('[name="_csrfToken"]').val());
},
将代码放在成功函数之前
当将字符串作为 POST 数据传递时,集成测试用例不会自动设置令牌,无论是 CSRF 令牌还是安全令牌,因为它无法在不知道数据格式。因此它也不会设置 cookie。
因此,在您传递字符串数据的情况下,您必须手动设置 cookie 和令牌,这与您链接的答案中描述的类似。但是,当使用 application/x-www-form-urlencoded
数据以外的任何数据(即 PHP 将解码并放入 $_POST
超全局的数据)时,在您的示例 JSON 数据中,您必须传递令牌作为 header,因为 JSON 输入数据将由请求处理程序组件解码(有计划将其移动到中间件层 IIRC),其中 运行s 在 CSRF 中间件之后,因此不会看到任何 post 数据。
示例:
$token = 'my-csrf-token';
$this->cookie('csrfToken', $token);
$this->configRequest([
'headers' => [
'X-CSRF-Token' => $token,
// ...
]
]);
另一方面,安全组件令牌必须进入 POST 数据,安全组件不会寻找 headers,并且它可以访问解码后的数据请求处理程序组件具有 运行(确保在 安全组件之前加载请求处理程序组件 !)。您可以参考 \Cake\TestSuite\IntegrationTestTrait::_addTokens()
源代码来了解安全令牌是如何构建的,您可以这样做:
$url = '/feedback/add';
$_data = [
'crash' => 1,
'details' => 'Lorem ipsum dolor sit amet'
];
$keys = array_map(
function ($field) {
return preg_replace('/(\.\d+)+$/', '', $field);
},
array_keys(Hash::flatten($_data))
);
$tokenData = $this->_buildFieldToken($url, array_unique($keys));
$_data['_Token'] = $tokenData;
$_data['_Token']['debug'] = 'SecurityComponent debug data would be added here';
请注意,传递给 _buildFieldToken()
的 URL 还必须包含可能的查询字符串数据!
tests/TestCase/Controller/FeedbackControllerTest.php:45
public function testAdd()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->session([
'Auth' => [
'User' => [
'id' => 1,
'role' => 'REPR',
]
]
]);
$this->configRequest([
'headers' => ['Accept' => 'application/json']
]);
$_data = [
'crash' => 1,
'details' => 'Lorem ipsum dolor sit amet'
];
$_data = json_encode($_data, JSON_PRETTY_PRINT);
$this->post('/feedback/add', $_data); // <---- 45
$expected = [
'status' => 'success'
];
$expected = json_encode($expected, JSON_PRETTY_PRINT);
$this->assertEquals($expected, (string)$this->_response->getBody());
}
PHP单元输出:
1) App\Test\TestCase\Controller\FeedbackControllerTest::testAdd
Cake\Http\Exception\InvalidCsrfTokenException: Missing CSRF token cookie
/vagrant/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:196
/vagrant/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:120
/vagrant/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:106
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:51
/vagrant/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php:168
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php:88
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php:96
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:51
/vagrant/vendor/cakephp/cakephp/src/Http/Server.php:98
/vagrant/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php:201
/vagrant/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php:516
/vagrant/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php:413
/vagrant/tests/TestCase/Controller/FeedbackControllerTest.php:45
我已经阅读并尝试了答案中的解决方案:
如果我像@ndm 说的那样添加:
$token = 'my-csrf-token';
$this->cookie('csrfToken', $token);
$data = [
'email' => 'info@example.com',
'password' => 'secret',
'_csrfToken' => $token
];
然后:
Cake\Http\Exception\InvalidCsrfTokenException: CSRF 令牌不匹配。
如何修复?
能否在 jquery ajax 函数中尝试这段代码,
beforeSend: function (xhr)
{
xhr.setRequestHeader('X-CSRF-Token', $('[name="_csrfToken"]').val());
},
将代码放在成功函数之前
当将字符串作为 POST 数据传递时,集成测试用例不会自动设置令牌,无论是 CSRF 令牌还是安全令牌,因为它无法在不知道数据格式。因此它也不会设置 cookie。
因此,在您传递字符串数据的情况下,您必须手动设置 cookie 和令牌,这与您链接的答案中描述的类似。但是,当使用 application/x-www-form-urlencoded
数据以外的任何数据(即 PHP 将解码并放入 $_POST
超全局的数据)时,在您的示例 JSON 数据中,您必须传递令牌作为 header,因为 JSON 输入数据将由请求处理程序组件解码(有计划将其移动到中间件层 IIRC),其中 运行s 在 CSRF 中间件之后,因此不会看到任何 post 数据。
示例:
$token = 'my-csrf-token';
$this->cookie('csrfToken', $token);
$this->configRequest([
'headers' => [
'X-CSRF-Token' => $token,
// ...
]
]);
另一方面,安全组件令牌必须进入 POST 数据,安全组件不会寻找 headers,并且它可以访问解码后的数据请求处理程序组件具有 运行(确保在 安全组件之前加载请求处理程序组件 !)。您可以参考 \Cake\TestSuite\IntegrationTestTrait::_addTokens()
源代码来了解安全令牌是如何构建的,您可以这样做:
$url = '/feedback/add';
$_data = [
'crash' => 1,
'details' => 'Lorem ipsum dolor sit amet'
];
$keys = array_map(
function ($field) {
return preg_replace('/(\.\d+)+$/', '', $field);
},
array_keys(Hash::flatten($_data))
);
$tokenData = $this->_buildFieldToken($url, array_unique($keys));
$_data['_Token'] = $tokenData;
$_data['_Token']['debug'] = 'SecurityComponent debug data would be added here';
请注意,传递给 _buildFieldToken()
的 URL 还必须包含可能的查询字符串数据!