使用 ajax 和获取 API 的 Slim PHP v3 CSRF
Slim PHP v3 CSRF with ajax and the fetch API
我已经设置了正常的 CSRF 东西等并且可以正常工作,但是当我去使用 ajax 使用 whatwg-fetch api (https://github.com/github/fetch)
现在一切似乎都很好,而且对我所拥有的一切都很好。但!然后我按如下方式添加 CSRF 设置,但始终失败:
所以,我使用了正常的,但它失败了,在 header 我收到消息:Failed CSRF check!
$app->add(new \Slim\Csrf\Guard);
但我想添加自己的消息等所以添加了以下内容,但仍然没有通过。
$container['csrf'] = function ($c) {
$guard = new \Slim\Csrf\Guard;
$guard->setFailureCallable(function ($request, $response, $next) {
$request = $request->withAttribute("csrf_status", false);
return $next($request, $response);
});
return $guard;
};
$app->add($container->get('csrf'));
然后在我的 class 中,我检查它:
if (false === $req->getAttribute('csrf_status')) {...}else{//all ok}
但无论发生什么,它总是失败。
在我的 js 中,我将令牌详细信息添加到请求中,例如:
fetch('/post/url',{
method: 'POST',
headers: {
'X-CSRF-Token': {
'csrf_name':csrf_name,
'csrf_value':csrf_value
}
},
body: new FormData(theForm)
我已经查看了发布的数据等,并且提交了表单数据,包括 csrf 值等。所以要求 csrf 数据是通过表单以及 header?
那么我怎样才能获得与 Slim CSRF 一起使用的 ajax 功能,我缺少什么?
提前致谢
好吧,在过去一天尝试了几次并将范围缩小到 fetch api
之后,我决定回到受信任的 jQuery aJax 方法,这似乎已经工作了。
似乎以下 body
和 new FormData()
没有被拾取:
fetch('/post/url',{
method: 'POST',
body: new FormData(theForm)
所以把它换成
$.ajax({
url : '/url/to/post',
type: "POST",
data: {key:value, kay:value}
一切都很好。
下一个要研究的问题是在第一次 ajax 调用时刷新键,防止再次调用,除非刷新页面,但那是另一天的事
我也无法获取以将令牌放入 body。我决定扩展 class 以便我可以修改 __invoke 方法。我添加了一些代码以从 headers 中提取 csrf。
现在在你的依赖项中使用这个 class。
$c['csrf'] = function ($c) {
return new \Foo\CSRF\Guard;
};
扩展class.
<?php
namespace MYOWN\CSRF;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* CSRF protection middleware.
*/
class Guard extends \Slim\Csrf\Guard
{
public function __construct(
$prefix = 'csrf',
&$storage = null,
callable $failureCallable = null,
$storageLimit = 200,
$strength = 16,
$persistentTokenMode = false
) {
parent::__construct(
$prefix,
$storage,
$failureCallable,
$storageLimit,
$strength,
$persistentTokenMode);
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$this->validateStorage();
// Validate POST, PUT, DELETE, PATCH requests
if (in_array($request->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
$body = $request->getParsedBody();
$body = $body ? (array)$body : [];
$name = isset($body[$this->prefix . '_name']) ? $body[$this->prefix . '_name'] : false;
$value = isset($body[$this->prefix . '_value']) ? $body[$this->prefix . '_value'] : false;
if (!empty($csrfTokens = $request->getHeader('x-csrf-token'))) {
$csrfTokens = json_decode($csrfTokens[0], true);
$name = isset($csrfTokens[$this->prefix . '_name']) ? $csrfTokens[$this->prefix . '_name'] : false;
$value = isset($csrfTokens[$this->prefix . '_value']) ? $csrfTokens[$this->prefix . '_value'] : false;
}
if (!$name || !$value || !$this->validateToken($name, $value)) {
// Need to regenerate a new token, as the validateToken removed the current one.
$request = $this->generateNewToken($request);
$failureCallable = $this->getFailureCallable();
return $failureCallable($request, $response, $next);
}
}
// Generate new CSRF token if persistentTokenMode is false, or if a valid keyPair has not yet been stored
if (!$this->persistentTokenMode || !$this->loadLastKeyPair()) {
$request = $this->generateNewToken($request);
}
// Enforce the storage limit
$this->enforceStorageLimit();
return $next($request, $response);
}
}
在阅读了一位创作者的其中一篇博客后,我再次尝试了这个。所以你可以忽略我之前的回答。
在 body 中发送这些 headers 中的 csrf 通过 csrf 检查。
const data = {
'csrf_name': csrf_name,
'csrf_value': csrf_value,
};
fetch(apiUrl, {
method: 'POST',
credentials: 'include',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json, application/x-www-form-urlencoded; charset=utf-8',
},
}).then((response) => {
if (response.ok) {
return response.json();
}
return null;
}).then((json) => {
console.log(json);
}).catch(() => {
});
最终帮助我成功使用 Slim PHP 和使用 fetch 时的 CSRF 值是将 credentials: 'include'
添加到 fetch 请求中,例如:
const body = JSON.stringify({
csrf_name: csrfName.value,
csrf_value: csrfValue.value
// You can add more data here
});
fetch('/some/request', {
method: 'POST',
body: body,
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
}).then(...)
我已经设置了正常的 CSRF 东西等并且可以正常工作,但是当我去使用 ajax 使用 whatwg-fetch api (https://github.com/github/fetch)
现在一切似乎都很好,而且对我所拥有的一切都很好。但!然后我按如下方式添加 CSRF 设置,但始终失败:
所以,我使用了正常的,但它失败了,在 header 我收到消息:Failed CSRF check!
$app->add(new \Slim\Csrf\Guard);
但我想添加自己的消息等所以添加了以下内容,但仍然没有通过。
$container['csrf'] = function ($c) {
$guard = new \Slim\Csrf\Guard;
$guard->setFailureCallable(function ($request, $response, $next) {
$request = $request->withAttribute("csrf_status", false);
return $next($request, $response);
});
return $guard;
};
$app->add($container->get('csrf'));
然后在我的 class 中,我检查它:
if (false === $req->getAttribute('csrf_status')) {...}else{//all ok}
但无论发生什么,它总是失败。
在我的 js 中,我将令牌详细信息添加到请求中,例如:
fetch('/post/url',{
method: 'POST',
headers: {
'X-CSRF-Token': {
'csrf_name':csrf_name,
'csrf_value':csrf_value
}
},
body: new FormData(theForm)
我已经查看了发布的数据等,并且提交了表单数据,包括 csrf 值等。所以要求 csrf 数据是通过表单以及 header?
那么我怎样才能获得与 Slim CSRF 一起使用的 ajax 功能,我缺少什么?
提前致谢
好吧,在过去一天尝试了几次并将范围缩小到 fetch api
之后,我决定回到受信任的 jQuery aJax 方法,这似乎已经工作了。
似乎以下 body
和 new FormData()
没有被拾取:
fetch('/post/url',{
method: 'POST',
body: new FormData(theForm)
所以把它换成
$.ajax({
url : '/url/to/post',
type: "POST",
data: {key:value, kay:value}
一切都很好。
下一个要研究的问题是在第一次 ajax 调用时刷新键,防止再次调用,除非刷新页面,但那是另一天的事
我也无法获取以将令牌放入 body。我决定扩展 class 以便我可以修改 __invoke 方法。我添加了一些代码以从 headers 中提取 csrf。
现在在你的依赖项中使用这个 class。
$c['csrf'] = function ($c) {
return new \Foo\CSRF\Guard;
};
扩展class.
<?php
namespace MYOWN\CSRF;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
/**
* CSRF protection middleware.
*/
class Guard extends \Slim\Csrf\Guard
{
public function __construct(
$prefix = 'csrf',
&$storage = null,
callable $failureCallable = null,
$storageLimit = 200,
$strength = 16,
$persistentTokenMode = false
) {
parent::__construct(
$prefix,
$storage,
$failureCallable,
$storageLimit,
$strength,
$persistentTokenMode);
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$this->validateStorage();
// Validate POST, PUT, DELETE, PATCH requests
if (in_array($request->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
$body = $request->getParsedBody();
$body = $body ? (array)$body : [];
$name = isset($body[$this->prefix . '_name']) ? $body[$this->prefix . '_name'] : false;
$value = isset($body[$this->prefix . '_value']) ? $body[$this->prefix . '_value'] : false;
if (!empty($csrfTokens = $request->getHeader('x-csrf-token'))) {
$csrfTokens = json_decode($csrfTokens[0], true);
$name = isset($csrfTokens[$this->prefix . '_name']) ? $csrfTokens[$this->prefix . '_name'] : false;
$value = isset($csrfTokens[$this->prefix . '_value']) ? $csrfTokens[$this->prefix . '_value'] : false;
}
if (!$name || !$value || !$this->validateToken($name, $value)) {
// Need to regenerate a new token, as the validateToken removed the current one.
$request = $this->generateNewToken($request);
$failureCallable = $this->getFailureCallable();
return $failureCallable($request, $response, $next);
}
}
// Generate new CSRF token if persistentTokenMode is false, or if a valid keyPair has not yet been stored
if (!$this->persistentTokenMode || !$this->loadLastKeyPair()) {
$request = $this->generateNewToken($request);
}
// Enforce the storage limit
$this->enforceStorageLimit();
return $next($request, $response);
}
}
在阅读了一位创作者的其中一篇博客后,我再次尝试了这个。所以你可以忽略我之前的回答。
在 body 中发送这些 headers 中的 csrf 通过 csrf 检查。
const data = {
'csrf_name': csrf_name,
'csrf_value': csrf_value,
};
fetch(apiUrl, {
method: 'POST',
credentials: 'include',
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json, application/x-www-form-urlencoded; charset=utf-8',
},
}).then((response) => {
if (response.ok) {
return response.json();
}
return null;
}).then((json) => {
console.log(json);
}).catch(() => {
});
最终帮助我成功使用 Slim PHP 和使用 fetch 时的 CSRF 值是将 credentials: 'include'
添加到 fetch 请求中,例如:
const body = JSON.stringify({
csrf_name: csrfName.value,
csrf_value: csrfValue.value
// You can add more data here
});
fetch('/some/request', {
method: 'POST',
body: body,
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
}).then(...)