Yii 1.1.17:CSRF 令牌验证失败 POST 通过 Angular 控制器
Yii 1.1.17: CSRF token validation fails with POST via Angular Controller
我有一个 Yii 应用程序,其中有一个 link(在表单外)触发一个 Angular 控制器 post 一些数据。问题是 Yii 在发生这种情况时不会验证 CSRF 令牌。
我的原始 url 看起来像这样:
<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 控制器调用一个 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 请求看起来像这样:
如果我禁用 CSRF 验证,调用就会成功。
有什么想法吗?
谢谢!
完整答案:
经过一些调查,我注意到 $_POST
(也是 Yii 的 $request->getPost()
)实际上是空的,即使 Angular 是 posting 数据。在 Whosebug 上阅读 this answer 似乎它实际上是一个与 Angular JS 及其默认行为 post 和 apllication/json
有关的问题(好吧,也许不完全是一个问题)。正如这个问题的标记答案所建议的那样,并根据 linked 答案的建议,我最终覆盖了 Yii 的 CHttpRequest
class 如下:
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
验证有效,我们需要 2 个条件:
客户端必须有资格发送带有 cookie 的请求并接受 cookie。
我们需要在请求中传递令牌。
你的情况的第二个条件不工作。您将 json
传递给请求正文,并且 Yii
试图从 post:
获取令牌
$userToken=$this->getPost($this->csrfTokenName);
要更改此行为,您需要覆盖 CHttpRequest
并更改配置文件以使用您的 Request
class,例如
'components' => array(
'request' => array(
'class' => 'application.components.HttpRequest',
'enableCsrfValidation' => true,
),
),
希望这有助于理解执行 CSRF 验证时发生的情况。
我有一个 Yii 应用程序,其中有一个 link(在表单外)触发一个 Angular 控制器 post 一些数据。问题是 Yii 在发生这种情况时不会验证 CSRF 令牌。
我的原始 url 看起来像这样:
<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 控制器调用一个 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 请求看起来像这样:
如果我禁用 CSRF 验证,调用就会成功。
有什么想法吗?
谢谢!
完整答案:
经过一些调查,我注意到 $_POST
(也是 Yii 的 $request->getPost()
)实际上是空的,即使 Angular 是 posting 数据。在 Whosebug 上阅读 this answer 似乎它实际上是一个与 Angular JS 及其默认行为 post 和 apllication/json
有关的问题(好吧,也许不完全是一个问题)。正如这个问题的标记答案所建议的那样,并根据 linked 答案的建议,我最终覆盖了 Yii 的 CHttpRequest
class 如下:
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
验证有效,我们需要 2 个条件:
客户端必须有资格发送带有 cookie 的请求并接受 cookie。
我们需要在请求中传递令牌。
你的情况的第二个条件不工作。您将 json
传递给请求正文,并且 Yii
试图从 post:
$userToken=$this->getPost($this->csrfTokenName);
要更改此行为,您需要覆盖 CHttpRequest
并更改配置文件以使用您的 Request
class,例如
'components' => array(
'request' => array(
'class' => 'application.components.HttpRequest',
'enableCsrfValidation' => true,
),
),
希望这有助于理解执行 CSRF 验证时发生的情况。