使用 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 方法,这似乎已经工作了。

似乎以下 bodynew 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(...)