diff --git a/docs/guide/runtime-sessions-cookies.md b/docs/guide/runtime-sessions-cookies.md
index b807799831..6ab4ae8492 100644
--- a/docs/guide/runtime-sessions-cookies.md
+++ b/docs/guide/runtime-sessions-cookies.md
@@ -332,11 +332,6 @@ examples, the [[yii\web\Cookie]] class also defines other properties to fully re
information, such as [[yii\web\Cookie::domain|domain]], [[yii\web\Cookie::expire|expire]]. You may configure these
properties as needed to prepare a cookie and then add it to the response's cookie collection.
-> Note: For better security, the default value of [[yii\web\Cookie::httpOnly]] is set to `true`. This helps mitigate
-the risk of a client-side script accessing the protected cookie (if the browser supports it). You may read
-the [httpOnly wiki article](https://www.owasp.org/index.php/HttpOnly) for more details.
-
-
### Cookie Validation
When you are reading and sending cookies through the `request` and `response` components as shown in the last
@@ -369,3 +364,34 @@ return [
> Info: [[yii\web\Request::cookieValidationKey|cookieValidationKey]] is critical to your application's security.
It should only be known to people you trust. Do not store it in the version control system.
+
+## Security settings
+
+Both [[yii\web\Cookie]] and [[yii\web\Session]] support the following security flags:
+
+### httpOnly
+
+For better security, the default value of [[yii\web\Cookie::httpOnly]] and the 'httponly' parameter of
+[[yii\web\Session::cookieParams]] is set to `true`. This helps mitigate the risk of a client-side script accessing
+the protected cookie (if the browser supports it).
+You may read the [HttpOnly wiki article](https://www.owasp.org/index.php/HttpOnly) for more details.
+
+### secure
+The purpose of the secure flag is to prevent cookies from being send in clear text. If the browser supports the
+secure flag it will only include the cookie when the request is sent over a secure (TLS) connection.
+You may read the [SecureFlag wiki article](https://www.owasp.org/index.php/SecureFlag) for more details.
+
+### sameSite
+Starting with Yii 2.0.21 the [[yii\web\Cookie::sameSite]] setting is supported. It requires PHP version 7.3.0 or higher.
+The purpose of the `sameSite` setting is to prevent CSRF (Cross-Site Request Forgery) attacks.
+If the browser supports the `sameSite` setting it will only include the cookie according to the specified policy ('Lax' or 'Strict').
+You may read the [SameSite wiki article](https://www.owasp.org/index.php/SameSite) for more details.
+For better security, an exception will be thrown if `sameSite` is used with an unsupported version of PHP.
+To use this feature across different PHP versions check the version first. E.g.
+```php
+[
+ 'sameSite' => PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null,
+]
+```
+> Note: Since not all browsers support the `sameSite` setting yet, it is still strongly recommended to also include
+ [additional CSRF protection](security-best-practices.md#avoiding-csrf).
diff --git a/docs/guide/security-best-practices.md b/docs/guide/security-best-practices.md
index 6e12299707..b9e1d01d04 100644
--- a/docs/guide/security-best-practices.md
+++ b/docs/guide/security-best-practices.md
@@ -253,9 +253,14 @@ class ContactAction extends Action
> Warning: Disabling CSRF will allow any site to send POST requests to your site. It is important to implement extra validation such as checking an IP address or a secret token in this case.
+> Note: Since version 2.0.21 Yii supports the `sameSite` cookie setting (requires PHP version 7.3.0 or higher).
+ Setting the `sameSite` cookie setting does not make the above obsolete since not all browsers support the setting yet.
+ See the [Sessions and Cookies sameSite option](runtime-sessions-cookies.md#samesite) for more information.
+
Further reading on the topic:
-
+-
Avoiding file exposure
@@ -301,6 +306,10 @@ provided by the H5BP project:
- [IIS](https://github.com/h5bp/server-configs-iis).
- [Lighttpd](https://github.com/h5bp/server-configs-lighttpd).
+> Note: When TLS is configured it is recommended that (session) cookies are send over TLS exclusively.
+ This is achieved by setting the `secure` flag for sessions and/or cookies.
+ See the [Sessions and Cookies secure flag](runtime-sessions-cookies.md#secure) for more information.
+
Secure Server configuration
---------------------------
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 99cdb9cc53..d5b5510bf8 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -7,6 +7,7 @@ Yii Framework 2 Change Log
- Bug #17341: Fixed error from yii.activeForm.js in strict mode (mikehaertl)
- Enh #17345: Improved performance of `yii\db\Connection::quoteColumnName()` (brandonkelly)
- Enh #17348: Improved performance of `yii\db\Connection::quoteTableName()` (brandonkelly)
+- Enh #17353: Added `sameSite` support for `yii\web\Cookie` and `yii\web\Session::cookieParams` (rhertogh)
2.0.20 June 04, 2019
diff --git a/framework/web/Cookie.php b/framework/web/Cookie.php
index 931a1ede1c..788db9e4aa 100644
--- a/framework/web/Cookie.php
+++ b/framework/web/Cookie.php
@@ -17,6 +17,23 @@ namespace yii\web;
*/
class Cookie extends \yii\base\BaseObject
{
+ /**
+ * SameSite policy Lax will prevent the cookie from being sent by the browser in all cross-site browsing context
+ * during CSRF-prone request methods (e.g. POST, PUT, PATCH etc).
+ * E.g. a POST request from https://otherdomain.com to https://yourdomain.com will not include the cookie, however a GET request will.
+ * When a user follows a link from https://otherdomain.com to https://yourdomain.com it will include the cookie
+ * @see $sameSite
+ */
+ const SAME_SITE_LAX = 'Lax';
+ /**
+ * SameSite policy Strict will prevent the cookie from being sent by the browser in all cross-site browsing context
+ * regardless of the request method and even when following a regular link.
+ * E.g. a GET request from https://otherdomain.com to https://yourdomain.com or a user following a link from
+ * https://otherdomain.com to https://yourdomain.com will not include the cookie.
+ * @see $sameSite
+ */
+ const SAME_SITE_STRICT = 'Strict';
+
/**
* @var string name of the cookie
*/
@@ -48,6 +65,19 @@ class Cookie extends \yii\base\BaseObject
* such as JavaScript, which can effectively help to reduce identity theft through XSS attacks.
*/
public $httpOnly = true;
+ /**
+ * @var string SameSite prevents the browser from sending this cookie along with cross-site requests.
+ * Please note that this feature is only supported since PHP 7.3.0
+ * For better security, an exception will be thrown if `sameSite` is set while using an unsupported version of PHP.
+ * To use this feature across different PHP versions check the version first. E.g.
+ * ```php
+ * $cookie->sameSite = PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null,
+ * ```
+ * See https://www.owasp.org/index.php/SameSite for more information about sameSite.
+ *
+ * @since 2.0.21
+ */
+ public $sameSite;
/**
diff --git a/framework/web/Response.php b/framework/web/Response.php
index 22a617cf07..61571476a4 100644
--- a/framework/web/Response.php
+++ b/framework/web/Response.php
@@ -401,7 +401,21 @@ class Response extends \yii\base\Response
if ($cookie->expire != 1 && isset($validationKey)) {
$value = Yii::$app->getSecurity()->hashData(serialize([$cookie->name, $value]), $validationKey);
}
- setcookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);
+ if (PHP_VERSION_ID >= 70300) {
+ setcookie($cookie->name, $value, [
+ 'expires' => $cookie->expire,
+ 'path' => $cookie->path,
+ 'domain' => $cookie->domain,
+ 'secure' => $cookie->secure,
+ 'httpOnly' => $cookie->httpOnly,
+ 'sameSite' => !empty($cookie->sameSite) ? $cookie->sameSite : null,
+ ]);
+ } else {
+ if (!is_null($cookie->sameSite)) {
+ throw new InvalidConfigException(get_class($cookie) . '::sameSite is not supported by PHP versions < 7.3.0 (set it to null in this environment)');
+ }
+ setcookie($cookie->name, $value, $cookie->expire, $cookie->path, $cookie->domain, $cookie->secure, $cookie->httpOnly);
+ }
}
}
diff --git a/framework/web/Session.php b/framework/web/Session.php
index 59aeade01d..0e47559146 100644
--- a/framework/web/Session.php
+++ b/framework/web/Session.php
@@ -367,6 +367,16 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
* The cookie parameters passed to this method will be merged with the result
* of `session_get_cookie_params()`.
* @param array $value cookie parameters, valid keys include: `lifetime`, `path`, `domain`, `secure` and `httponly`.
+ * Starting with Yii 2.0.21 `sameSite` is also supported. It requires PHP version 7.3.0 or higher.
+ * For securtiy, an exception will be thrown if `sameSite` is set while using an unsupported version of PHP.
+ * To use this feature across different PHP versions check the version first. E.g.
+ * ```php
+ * [
+ * 'sameSite' => PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null,
+ * ]
+ * ```
+ * See https://www.owasp.org/index.php/SameSite for more information about `sameSite`.
+ *
* @throws InvalidArgumentException if the parameters are incomplete.
* @see https://secure.php.net/manual/en/function.session-set-cookie-params.php
*/
@@ -385,7 +395,15 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
{
$data = $this->getCookieParams();
if (isset($data['lifetime'], $data['path'], $data['domain'], $data['secure'], $data['httponly'])) {
- session_set_cookie_params($data['lifetime'], $data['path'], $data['domain'], $data['secure'], $data['httponly']);
+ if (PHP_VERSION_ID >= 70300) {
+ session_set_cookie_params($data);
+ } else {
+ if (!empty($data['sameSite'])) {
+ throw new InvalidConfigException('sameSite cookie is not supported by PHP versions < 7.3.0 (set it to null in this environment)');
+ }
+ session_set_cookie_params($data['lifetime'], $data['path'], $data['domain'], $data['secure'], $data['httponly']);
+ }
+
} else {
throw new InvalidArgumentException('Please make sure cookieParams contains these elements: lifetime, path, domain, secure and httponly.');
}