php - 伊伊 1.1.17 : CSRF token validation fails with POST via Angular Controller

标签 php angularjs ajax yii csrf

我有一个 Yii 应用程序,其中有一个链接(在表单之外)触发 Angular Controller 发布一些数据。问题在于,发生这种情况时,Yii 不会验证 CSRF token 。

我的原始网址如下所示:

<a id="yt1" href="#" ng-click="markAllAsRead(23, '1eb4e3ac755e22939a0fc8d5ea0e9bacb453319a')" title="Read All" tooltips-size="small" tooltips="1" class="notification-tool ng-isolate-scope"><i class="fa fa-eye"></i></a>

我的 Angular Controller 调用一个 Angular 服务,如下所示:

notificationsService.markAllAsRead = function (user_id, csrf) {
        var dataObject = {
            user_id: user_id,
            YII_CSRF_TOKEN: csrf
        };

        $http.post("/api/notifications/readAll", dataObject).success(function (data) {
            return data;
        });
    };

POST 请求看起来像这样: POST request via ajax

如果我禁用 CSRF 验证,调用就会成功。

有什么想法吗?

谢谢!

完整答案:

经过一番调查,我注意到 $_POST (也是 Yii 的 $request->getPost())实际上是空的,尽管 Angular 正在发布数据。正在阅读this answer在 stackoverflow 上,似乎这实际上是一个与 Angular JS 及其以 apllication/json 形式发布的默认行为有关的问题(好吧,也许不完全是一个问题)。正如这个问题的标记答案所建议的,并根据链接答案的建议,我最终覆盖了 Yii 中的 CHttpRequest 类,如下所示:

class AppRequest extends CHttpRequest
{
    public function validateCsrfToken($event)
    {
        if ($this->getIsPostRequest() ||
            $this->getIsPutRequest() ||
            $this->getIsPatchRequest() ||
            $this->getIsDeleteRequest()
        ) {
            $cookies = $this->getCookies();

            $method = $this->getRequestType();
            switch ($method) {
                case 'POST':
                    if (empty($this->getPost($this->csrfTokenName))) {
                        $input = json_decode(file_get_contents('php://input'), true);;
                        $userToken = $input[$this->csrfTokenName];
                    } else {
                        $userToken = $this->getPost($this->csrfTokenName);
                    }
                    break;
                case 'PUT':
                    if (empty($this->getPut($this->csrfTokenName))) {
                        $input = json_decode(file_get_contents('php://input'), true);;
                        $userToken = $input[$this->csrfTokenName];
                    } else {
                        $userToken = $this->getPut($this->csrfTokenName);
                    }
                    break;
                case 'PATCH':
                    if (empty($this->getPatch($this->csrfTokenName))) {
                        $input = json_decode(file_get_contents('php://input'), true);;
                        $userToken = $input[$this->csrfTokenName];
                    } else {
                        $userToken = $this->getPatch($this->csrfTokenName);
                    }
                    break;
                case 'DELETE':
                    if (empty($this->getDelete($this->csrfTokenName))) {
                        $input = json_decode(file_get_contents('php://input'), true);;
                        $userToken = $input[$this->csrfTokenName];
                    } else {
                        $userToken = $this->getDelete($this->csrfTokenName);
                    }
                    break;
            }

            if (!empty($userToken) && $cookies->contains($this->csrfTokenName)) {
                $cookieToken = $cookies->itemAt($this->csrfTokenName)->value;
                $valid = $cookieToken === $userToken;
            } else
                $valid = false;
            if (!$valid)
                throw new CHttpException(400, Yii::t('yii', 'The CSRF token could not be verified.'));
        }
    }
}

最佳答案

如果您查看CHttpRequest并观察验证的执行方式,您就会理解问题。

public function validateCsrfToken($event)
{
    if ($this->getIsPostRequest() ||
        $this->getIsPutRequest() ||
        $this->getIsPatchRequest() ||
        $this->getIsDeleteRequest())
    {
        $cookies=$this->getCookies();
        $method=$this->getRequestType();
        switch($method)
        {
            case 'POST':
                $userToken=$this->getPost($this->csrfTokenName);
            break;
            case 'PUT':
                $userToken=$this->getPut($this->csrfTokenName);
            break;
            case 'PATCH':
                $userToken=$this->getPatch($this->csrfTokenName);
            break;
            case 'DELETE':
                $userToken=$this->getDelete($this->csrfTokenName);
        }
        if (!empty($userToken) && $cookies->contains($this->csrfTokenName))
        {
            $cookieToken=$cookies->itemAt($this->csrfTokenName)->value;
            $valid=$cookieToken===$userToken;
        }
        else
            $valid = false;
        if (!$valid)
            throw new CHttpException(400,Yii::t('yii','The CSRF token could not be verified.'));
    }
}

因此,要使 CSRF 验证正常工作,我们需要两个条件:

  1. 客户端必须有资格发送带有 cookie 的请求并接受 cookie。

  2. 我们需要通过请求传递 token 。

您的情况的第二个条件不起作用。您将 json 传递给请求正文,并且 Yii 尝试从帖子中获取 token :

$userToken=$this->getPost($this->csrfTokenName);

要更改此行为,您需要覆盖 CHttpRequest 并更改配置文件以使用您的 Request 类,例如

  'components' => array(
    'request' => array(
        'class' => 'application.components.HttpRequest',
        'enableCsrfValidation' => true,
    ),
  ),

希望这将有助于理解执行 CSRF 验证时发生的情况。

关于php - 伊伊 1.1.17 : CSRF token validation fails with POST via Angular Controller,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36207972/

相关文章:

php - 使用对象作为参数与在方法中创建对象

php - 独立的 getter/setter 方法,还是组合?

javascript - 如何等待 $firebaseArray 数据

javascript - JsFiddle : Unknown provider: $intervalProvider <- $interval

javascript - Ajax 成功函数未使用 require 指令调用

php - 从我网站上可用的头像图像更改用户头像?

php - 检查每天使用 PHP 更新的大型 XML 文件(100k+ 条目)中的更改的最快、最有效的方法

javascript - 如何编写服务于大多数本地文件但将某些文件重新路由到另一个域的 Node 快速应用程序?

javascript - 最佳实践 : How to authenticate/authorize AJAX Calls on the server

java - JSP + Ajax 不起作用