[英]Yii 1.1.17: CSRF token validation fails with POST via Angular Controller
我有一個Yii應用程序,其中有一個鏈接(窗體外部)觸發一個發布一些數據的Angular控制器。 問題是Yii不會在發生這種情況時驗證CSRF令牌。
我的原始網址看起來像這樣:
<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;
});
};
如果我禁用CSRF驗證,則調用成功。
有任何想法嗎?
謝謝!
完整答案:
經過一番調查之后,我注意到$_POST
(也是Yii的$request->getPost()
)實際上是空的,即使Angular正在發布數據。 閱讀關於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
驗證有效,我們需要兩個條件:
客戶必須符合發送帶有cookie的請求並接受cookie的資格。
我們需要在請求中傳遞令牌。
您的情況下的第二個條件不起作用。 您正在將json
傳遞給請求正文,而Yii
試圖從帖子中獲取令牌:
$userToken=$this->getPost($this->csrfTokenName);
要更改此行為,您需要覆蓋CHttpRequest
並更改配置文件以使用您的Request
類,例如
'components' => array(
'request' => array(
'class' => 'application.components.HttpRequest',
'enableCsrfValidation' => true,
),
),
希望這將有助於了解執行CSRF驗證時發生的情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.