NEW Allow setting "samesite" attribute for individual cookies.#11632
Conversation
| private bool $tokenCookieSecure = true; | ||
|
|
||
| /** | ||
| * @var boolean | ||
| * The SameSite value to use for authentication cookies. | ||
| * If set to an empty string, the default value from Cookie.default_samesite will be used. | ||
| */ | ||
| private $tokenCookieSecure = false; | ||
| private string $tokenCookieSameSite = Cookie::SAMESITE_STRICT; |
There was a problem hiding this comment.
In https://docs.silverstripe.org/en/6/developer_guides/security/secure_coding/#secure-sessions-and-cookies when talking about updating the session cookie security it says
The same treatment should be applied to the cookie responsible for remembering logins across sessions
For this reason I've chosen to do exactly that - I've updated its default secure and samesite values to match the session cookie values.
| */ | ||
| public function getTokenCookieSecure() | ||
| public function getTokenCookieSecure(): bool | ||
| { | ||
| if ($this->getTokenCookieSameSite() === Cookie::SAMESITE_NONE) { | ||
| return true; | ||
| } | ||
| return $this->tokenCookieSecure; | ||
| } |
There was a problem hiding this comment.
Matches how "secure" is set for session cookies
|
|
||
| public const SAMESITE_LAX = 'Lax'; | ||
|
|
||
| public const SAMESITE_STRICT = 'Strict'; | ||
|
|
||
| public const SAMESITE_LAX = 'Lax'; | ||
|
|
||
| public const SAMESITE_NONE = 'None'; |
There was a problem hiding this comment.
Updated the order so it's most to least secure
| * @var bool | ||
| */ | ||
| private static $report_errors = true; | ||
| private static bool $report_errors = true; |
There was a problem hiding this comment.
A fair amount of unrelated strict typing being added in this PR
| string $name, | ||
| string|false $value, | ||
| int $expiry = 90, | ||
| ?string $path = null, | ||
| ?string $domain = null, | ||
| bool $secure = false, | ||
| bool $httpOnly = true, | ||
| string $sameSite = '' |
There was a problem hiding this comment.
$valuecan only be a string orfalseif you trace through the calls in the actual implementation.$expirycan only beint.- Chose to keep the nullability of
$pathand$domainto reduce the change of unexpected side effects and regressions given we're so close to the beta.
There was a problem hiding this comment.
These types are shared with CookieJar and Cookie_Backend as well.
| public static function force_expiry( | ||
| string $name, | ||
| ?string $path = null, | ||
| ?string $domain = null, | ||
| bool $secure = false, | ||
| bool $httpOnly = true, | ||
| string $sameSite = '' | ||
| ): void { | ||
| Cookie::get_inst()->forceExpiry($name, $path, $domain, $secure, $httpOnly, $sameSite); | ||
| } |
There was a problem hiding this comment.
According to https://www.php.net/manual/en/function.setcookie.php#refsect1-function.setcookie-notes
Cookies must be deleted with the same parameters as they were set with.
So it seems like we need to allow setting samesite here.
| * | ||
| * @param array $cookies The existing cookies to load into the cookie jar. | ||
| * Omit this to default to $_COOKIE | ||
| * @inheritDoc |
There was a problem hiding this comment.
Removed a lot of duplicate PHPDoc from this class
| $sameSite = static::config()->get('cookie_samesite') ?? Cookie::SAMESITE_LAX; | ||
| $sameSite = static::config()->get('cookie_samesite') ?? Cookie::SAMESITE_STRICT; |
There was a problem hiding this comment.
This and any other changes from lax to strict in this class are necessary as part of #11597
There was a problem hiding this comment.
Should move this logic to a new method Session::getCookieSamesite()
There was a problem hiding this comment.
It's only used in one place so that seems excessive - but done anyway. Private method so we can just axe it later if we want.
| $path = $this->config()->get('cookie_path') ?: Director::baseURL(); | ||
| $domain = $this->config()->get('cookie_domain'); | ||
| $secure = Director::is_https($request) && $this->config()->get('cookie_secure'); | ||
| Cookie::force_expiry(session_name(), $path, $domain, $secure, true); | ||
| $cookieParams = $this->buildCookieParams($request); | ||
| Cookie::force_expiry( | ||
| session_name(), | ||
| $cookieParams['path'], | ||
| $cookieParams['domain'], | ||
| $cookieParams['secure'], | ||
| true, | ||
| $cookieParams['samesite'] | ||
| ); |
There was a problem hiding this comment.
No sense rebuilding the params - updated this to use buildCookieParams()
| * @config | ||
| * @var String If this is set, then a session cookie with the given name will be set on log-in, | ||
| * and cleared on logout. | ||
| * This cookie shares the same SameSite and Secure parameters as the main session cookie. |
There was a problem hiding this comment.
See SessionAuthenticationHandler
|
Based on the existing tests it doesn't look like there's a sensible way to test this new parameter is actually coming through when cookies get set. |
bc01ee8 to
9ad275d
Compare
| * @param string $path The path to save the cookie on (falls back to site base) | ||
| * @param string $domain The domain to make the cookie available on | ||
| * @param boolean $secure Can the cookie only be sent over SSL? | ||
| * @param boolean $httpOnly Prevent the cookie being accessible by JS |
There was a problem hiding this comment.
Shouldn't be deleting docblock params when they're explaining something - e.g. $expiry and $path are non obvious.
This applies other instances of deleting docblock params in this PR
There was a problem hiding this comment.
Note the @inheritdoc immediately below the text you selected. These are duplicate PHPDocs as mentioned in #11632 (comment)
| string $sameSite = '' | ||
| ): void { | ||
| if ($sameSite === '') { | ||
| $sameSite = Cookie::config()->get('default_samesite') ?? Cookie::SAMESITE_LAX; |
There was a problem hiding this comment.
This should logic should be on a new method Cookie::getDefaultSameSite()
| string $sameSite = '' | ||
| ): bool { | ||
| if ($sameSite === '') { | ||
| $sameSite = Cookie::config()->get('default_samesite') ?? Cookie::SAMESITE_LAX; |
There was a problem hiding this comment.
This should logic should be on a new method Cookie::getDefaultSameSite()
| $sameSite = static::config()->get('cookie_samesite') ?? Cookie::SAMESITE_LAX; | ||
| $sameSite = static::config()->get('cookie_samesite') ?? Cookie::SAMESITE_STRICT; |
There was a problem hiding this comment.
Should move this logic to a new method Session::getCookieSamesite()
| public function getDeviceCookieName(): string | ||
| { | ||
| return $this->deviceCookieName; | ||
| return $this->deviceCookieName ?? ''; |
There was a problem hiding this comment.
| return $this->deviceCookieName ?? ''; | |
| return $this->deviceCookieName; |
Won't PHP complain about trying to access an un-initilised value?
Seems like it would be better to just give the property a default value of blank string
There was a problem hiding this comment.
No, it only does that for properties which are explicitly typed. But I guess I can make it explicitly string.
| public function getTokenCookieName(): string | ||
| { | ||
| return $this->tokenCookieName; | ||
| return $this->tokenCookieName ?? ''; |
There was a problem hiding this comment.
| return $this->tokenCookieName ?? ''; | |
| return $this->tokenCookieName; |
Same as getDeviceCookieName() above
| * @param ?string $path The path to save the cookie on (falls back to site base) | ||
| * @param ?string $domain The domain to make the cookie available on |
There was a problem hiding this comment.
| * @param ?string $path The path to save the cookie on (falls back to site base) | |
| * @param ?string $domain The domain to make the cookie available on | |
| * @param null|string $path The path to save the cookie on (falls back to site base) | |
| * @param null|string $domain The domain to make the cookie available on |
Don't think I've seen ? in a docblock param before, should be consistent
9ad275d to
f200906
Compare
|
CI failures are pre-existing and will be resolved in #11635 |
Also update secure-ness of authentication cookies to match session cookies, and finish securing session cookies.
f200906 to
33a6a13
Compare
Also update secure-ness of authentication cookies to match session cookies, and finish securing session cookies.
Issue