mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-26 14:26:54 +08:00
Added yii\web\Response::enableCsrfCookie
to support storing CSRF tokens in session
This commit is contained in:
@ -210,6 +210,7 @@ Yii Framework 2 Change Log
|
|||||||
- Enh: Added support for array attributes in `in` validator (creocoder)
|
- Enh: Added support for array attributes in `in` validator (creocoder)
|
||||||
- Enh: Improved `yii\helpers\Inflector::slug` to support more cases for Russian, Hebrew and special characters (samdark)
|
- Enh: Improved `yii\helpers\Inflector::slug` to support more cases for Russian, Hebrew and special characters (samdark)
|
||||||
- Enh: ListView now uses the widget ID in the base tag, consistent to gridview (cebe)
|
- Enh: ListView now uses the widget ID in the base tag, consistent to gridview (cebe)
|
||||||
|
- Enh: Added `yii\web\Response::enableCsrfCookie` to support storing CSRF tokens in session (qiangxue)
|
||||||
- Chg #2287: Split `yii\db\ColumnSchema::typecast()` into two methods `phpTypecast()` and `dbTypecast()` to allow specifying PDO type explicitly (cebe)
|
- Chg #2287: Split `yii\db\ColumnSchema::typecast()` into two methods `phpTypecast()` and `dbTypecast()` to allow specifying PDO type explicitly (cebe)
|
||||||
- Chg #2898: `yii\console\controllers\AssetController` is now using hashes instead of timestamps (samdark)
|
- Chg #2898: `yii\console\controllers\AssetController` is now using hashes instead of timestamps (samdark)
|
||||||
- Chg #2913: RBAC `DbManager` is now initialized via migration (samdark)
|
- Chg #2913: RBAC `DbManager` is now initialized via migration (samdark)
|
||||||
|
@ -71,6 +71,28 @@ yii = (function ($) {
|
|||||||
return $('meta[name=csrf-token]').prop('content');
|
return $('meta[name=csrf-token]').prop('content');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the CSRF token in the meta elements.
|
||||||
|
* This method is provided so that you can update the CSRF token with the latest one you obtain from the server.
|
||||||
|
* @param name the CSRF token name
|
||||||
|
* @param value the CSRF token value
|
||||||
|
*/
|
||||||
|
setCsrfToken: function (name, value) {
|
||||||
|
$('meta[name=csrf-param]').prop('content', name);
|
||||||
|
$('meta[name=csrf-token]').prop('content', value)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates all form CSRF input fields with the latest CSRF token.
|
||||||
|
* This method is provided to avoid cached forms containing outdated CSRF tokens.
|
||||||
|
*/
|
||||||
|
refreshCsrfToken: function () {
|
||||||
|
var token = pub.getCsrfToken();
|
||||||
|
if (token) {
|
||||||
|
$('form input[name="' + pub.getCsrfParam() + '"]').val(token);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays a confirmation dialog.
|
* Displays a confirmation dialog.
|
||||||
* The default implementation simply displays a js confirmation dialog.
|
* The default implementation simply displays a js confirmation dialog.
|
||||||
@ -211,6 +233,7 @@ yii = (function ($) {
|
|||||||
xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());
|
xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
pub.refreshCsrfToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
function initDataMethods() {
|
function initDataMethods() {
|
||||||
|
@ -115,10 +115,16 @@ class Request extends \yii\base\Request
|
|||||||
*/
|
*/
|
||||||
public $csrfParam = '_csrf';
|
public $csrfParam = '_csrf';
|
||||||
/**
|
/**
|
||||||
* @var array the configuration of the CSRF cookie. This property is used only when [[enableCsrfValidation]] is true.
|
* @var array the configuration for creating the CSRF [[Cookie|cookie]]. This property is used only when
|
||||||
* @see Cookie
|
* both [[enableCsrfValidation]] and [[enableCsrfCookie]] are true.
|
||||||
*/
|
*/
|
||||||
public $csrfCookie = ['httpOnly' => true];
|
public $csrfCookie = ['httpOnly' => true];
|
||||||
|
/**
|
||||||
|
* @var boolean whether to use cookie to persist CSRF token. If false, CSRF token will be stored
|
||||||
|
* in session under the name of [[csrfParam]]. Note that while storing CSRF tokens in session increases
|
||||||
|
* security, it requires starting a session for every page, which will degrade your site performance.
|
||||||
|
*/
|
||||||
|
public $enableCsrfCookie = true;
|
||||||
/**
|
/**
|
||||||
* @var boolean whether cookies should be validated to ensure they are not tampered. Defaults to true.
|
* @var boolean whether cookies should be validated to ensure they are not tampered. Defaults to true.
|
||||||
*/
|
*/
|
||||||
@ -1227,29 +1233,6 @@ class Request extends \yii\base\Request
|
|||||||
return $cookies;
|
return $cookies;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Cookie
|
|
||||||
*/
|
|
||||||
private $_csrfCookie;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the unmasked random token used to perform CSRF validation.
|
|
||||||
* This token is typically sent via a cookie. If such a cookie does not exist, a new token will be generated.
|
|
||||||
* @return string the random token for CSRF validation.
|
|
||||||
* @see enableCsrfValidation
|
|
||||||
*/
|
|
||||||
public function getRawCsrfToken()
|
|
||||||
{
|
|
||||||
if ($this->_csrfCookie === null) {
|
|
||||||
$this->_csrfCookie = $this->getCookies()->get($this->csrfParam);
|
|
||||||
if ($this->_csrfCookie === null || empty($this->_csrfCookie->value)) {
|
|
||||||
$this->_csrfCookie = $this->createCsrfCookie();
|
|
||||||
Yii::$app->getResponse()->getCookies()->add($this->_csrfCookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $this->_csrfCookie->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private $_csrfToken;
|
private $_csrfToken;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1258,17 +1241,19 @@ class Request extends \yii\base\Request
|
|||||||
* This token is a masked version of [[rawCsrfToken]] to prevent [BREACH attacks](http://breachattack.com/).
|
* This token is a masked version of [[rawCsrfToken]] to prevent [BREACH attacks](http://breachattack.com/).
|
||||||
* This token may be passed along via a hidden field of an HTML form or an HTTP header value
|
* This token may be passed along via a hidden field of an HTML form or an HTTP header value
|
||||||
* to support CSRF validation.
|
* to support CSRF validation.
|
||||||
*
|
* @param boolean $regenerate whether to regenerate CSRF token. When this parameter is true, each time
|
||||||
|
* this method is called, a new CSRF token will be generated and persisted (in session or cookie).
|
||||||
* @return string the token used to perform CSRF validation.
|
* @return string the token used to perform CSRF validation.
|
||||||
*/
|
*/
|
||||||
public function getCsrfToken()
|
public function getCsrfToken($regenerate = false)
|
||||||
{
|
{
|
||||||
if ($this->_csrfToken === null) {
|
if ($this->_csrfToken === null || $regenerate) {
|
||||||
|
if ($regenerate || ($token = $this->loadCsrfToken()) === null) {
|
||||||
|
$token = $this->generateCsrfToken();
|
||||||
|
}
|
||||||
// the mask doesn't need to be very random
|
// the mask doesn't need to be very random
|
||||||
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';
|
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';
|
||||||
$mask = substr(str_shuffle(str_repeat($chars, 5)), 0, self::CSRF_MASK_LENGTH);
|
$mask = substr(str_shuffle(str_repeat($chars, 5)), 0, self::CSRF_MASK_LENGTH);
|
||||||
|
|
||||||
$token = $this->getRawCsrfToken();
|
|
||||||
// The + sign may be decoded as blank space later, which will fail the validation
|
// The + sign may be decoded as blank space later, which will fail the validation
|
||||||
$this->_csrfToken = str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask)));
|
$this->_csrfToken = str_replace('+', '.', base64_encode($mask . $this->xorTokens($token, $mask)));
|
||||||
}
|
}
|
||||||
@ -1276,6 +1261,39 @@ class Request extends \yii\base\Request
|
|||||||
return $this->_csrfToken;
|
return $this->_csrfToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the CSRF token from cookie or session.
|
||||||
|
* @return string the CSRF token loaded from cookie or session. Null is returned if the cookie or session
|
||||||
|
* does not have CSRF token.
|
||||||
|
*/
|
||||||
|
protected function loadCsrfToken()
|
||||||
|
{
|
||||||
|
if ($this->enableCsrfCookie) {
|
||||||
|
return $this->getCookies()->getValue($this->csrfParam);
|
||||||
|
} else {
|
||||||
|
return Yii::$app->getSession()->get($this->csrfParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an unmasked random token used to perform CSRF validation.
|
||||||
|
* @return string the random token for CSRF validation.
|
||||||
|
*/
|
||||||
|
protected function generateCsrfToken()
|
||||||
|
{
|
||||||
|
$token = Yii::$app->getSecurity()->generateRandomString();
|
||||||
|
if ($this->enableCsrfCookie) {
|
||||||
|
$config = $this->csrfCookie;
|
||||||
|
$config['name'] = $this->csrfParam;
|
||||||
|
$config['value'] = $token;
|
||||||
|
Yii::$app->getResponse()->getCookies()->add(new Cookie($config));
|
||||||
|
} else {
|
||||||
|
$token = Yii::$app->getSecurity()->generateRandomString();
|
||||||
|
Yii::$app->getSession()->set($this->csrfParam, $token);
|
||||||
|
}
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the XOR result of two strings.
|
* Returns the XOR result of two strings.
|
||||||
* If the two strings are of different lengths, the shorter one will be padded to the length of the longer one.
|
* If the two strings are of different lengths, the shorter one will be padded to the length of the longer one.
|
||||||
@ -1333,10 +1351,10 @@ class Request extends \yii\base\Request
|
|||||||
if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
|
if (!$this->enableCsrfValidation || in_array($method, ['GET', 'HEAD', 'OPTIONS'], true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$trueToken = $this->getCookies()->getValue($this->csrfParam);
|
|
||||||
$token = $this->getBodyParam($this->csrfParam);
|
|
||||||
|
|
||||||
return $this->validateCsrfTokenInternal($token, $trueToken)
|
$trueToken = $this->loadCsrfToken();
|
||||||
|
|
||||||
|
return $this->validateCsrfTokenInternal($this->getBodyParam($this->csrfParam), $trueToken)
|
||||||
|| $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
|
|| $this->validateCsrfTokenInternal($this->getCsrfTokenFromHeader(), $trueToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user