-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
Add SameSite cookies to FrameWorkBundle #28168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
2051275
to
fa6ce44
Compare
4505b9a
to
559036a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing in NativeSessionStorageTest.php:
@@ -170,6 +170,9 @@ class NativeSessionStorageTest extends TestCase
'cookie_secure' => true,
'cookie_httponly' => false,
);
+ if (\PHP_VERSION_ID >= 70300) {
+ $options['cookie_samesite'] = '';
+ }
$this->getStorage($options);
} | ||
} else { | ||
$cookie = HeaderUtils::popSessionCookie($this->sessionName, $sessionId); | ||
if (null === $cookie) { | ||
setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need for samesite here also?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sets the session id to an empty value in the cookie, meaning the session won't work. I don't think adding SameSite here would add anything.
Looking at this some more, isn't this code supposed to remove the cookie entirely? In that case the value 0
is incorrect, that should be e.g. time() - 3600
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this code supposed to remove the cookie entirely
indeed, my bad
the value 0 is incorrect
I don't understand why 0 is invalid here. It's a timestamp in the past that's fine for removing a cookie AFAIK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the manual on setcookie:
If set to 0, or omitted, the cookie will expire at the end of the session (when the browser closes).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oups, would you mind opening a PR on 3.4 where this has been introduced?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem, I'll do that later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we actually need a calculated value here, or wouldn't 1
be sufficient? (Edit: maybe with a comment explaining it)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll test it locally to be sure, but it seems to me that 1
would work 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I tested this locally and the results are not as expected. When I send a cookie with an empty value and an expires
of 0 it actually does delete the cookie. Turns out further down the manual page it says:
If the value argument is an empty string, or FALSE, and all other arguments match a previous call to setcookie, then the cookie with the specified name will be deleted from the remote client. This is internally achieved by setting value to 'deleted' and expiration time to one year in past.
Which is actually only partly true from what I've seen as:
- It doesn't set the expiration time to one year in the past, it sets it to 1970-01-01 00:00:01
- The value of
$httponly
doesn't seem to matter; If a cookie was set with httpfalse
and you delete it withhttp
true it gets deleted all the same, even though the documentation suggests it would not - If I create a cookie with SameSite=lax and remove it without any SameSite option, it still deletes the cookie
It does however always send a Set-Cookie
header, it's just that it doesn't get picked up by the browser as a command to delete the previous cookie.
I've only tested in chrome, so the actual behaviour of when the header is honoured and when not might vary between browsers as well.
Maybe it would be best to add ini_get('session.cookie_samesite')
to the call if it's PHP >= 7.3 just to be sure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added the samesite
parameter for PHP >= 7.3 just to be sure.
@@ -359,6 +374,12 @@ public function setOptions(array $options) | |||
|
|||
foreach ($options as $key => $value) { | |||
if (isset($validOptions[$key])) { | |||
if ('cookie_samesite' === $key && \PHP_VERSION_ID < 703000) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
too many zeros: 70300
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, will fix that 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
559036a
to
44c8458
Compare
I've added the |
* Find the session header amongst the headers that are to be sent, remove it, and return | ||
* it so the caller can process it further. | ||
*/ | ||
public static function popSessionCookie(string $sessionName, string $sessionId): ?string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is really different from the other ones, as it does not deal with a header value, but with PHP headers themselves. So I would not expose it here as a public utility. I would keep it as @internal
somewhere else (especially given than one of the 2 places using it will disappear when dropping support for PHP 7.2 and older)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Any suggestion on where to keep it? Maybe introduce a Symfony\HttpFoundation\Session\SessionUtils
class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stof I've created a new Symfony\HttpFoundation\Session\SessionUtils
class, marked it as internal
and moved it there. Does that seem better to you?
@@ -359,6 +374,12 @@ public function setOptions(array $options) | |||
|
|||
foreach ($options as $key => $value) { | |||
if (isset($validOptions[$key])) { | |||
if ('cookie_samesite' === $key && \PHP_VERSION_ID < 70300) { | |||
// PHP <= 7.3 does not support same_site cookies. We will emulate it in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(obvious but anyway:) PHP < 7.3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. Fixed.
156c923
to
b0cce6c
Compare
/** | ||
* Session utility functions. | ||
* | ||
* @author Nicolas Grekas <[email protected]> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nicolas-grekas I've added you here since this was originally implemented by you but I've moved it here. Is that ok?
b0cce6c
to
69b17f1
Compare
src/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php
Show resolved
Hide resolved
I've just added the change to |
@@ -482,6 +483,7 @@ private function addSessionSection(ArrayNodeDefinition $rootNode) | |||
->scalarNode('cookie_domain')->end() | |||
->booleanNode('cookie_secure')->end() | |||
->booleanNode('cookie_httponly')->defaultTrue()->end() | |||
->enumNode('cookie_samesite')->values(array(null, Cookie::SAMESITE_LAX, Cookie::SAMESITE_STRICT))->defaultNull()->end() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about setting it to lax by default? this would improve the security of all Symfony apps by default
alternatively, we should make it the default in the flex recipe
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be a BC break as it could break some apps that post across domains by design.
Setting it as default in the flex recipe sounds good though!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this use case really exist? It would be great to improve the security of everyone by default if not. Worth thinking twice here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just put it in the flex recipe for now and default it to Lax in the framework for Symfony 5?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we really think this is a BC break, we cannot change it in 5 without first issuing a notice in 4...
Can you add a strong recommendation in the upgrading file also btw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rpkamp, could you please add a note in the upgrading files (4.2&5.0) to suggest enabling the lax mode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I will do that later today or else tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nicolas-grekas done. There are no UPGRADE-4.2.md
yet, so I've created one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never mind, there are already was an UPGRADE-4.2.md
on master, I've just rebased against master.
69b17f1
to
432f3be
Compare
Uses `session.cookie_samesite` for PHP >= 7.3. For PHP < 7.3 it first does a session_start(), find the emitted header, changes it, and emits it again with the value for SameSite added.
432f3be
to
4091feb
Compare
I'm not sure why Travis is failing now. It seems to be missing the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice (yes, master is broken for now.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with a small comment regarding CS
Thank you @rpkamp. |
This PR was merged into the 4.2-dev branch. Discussion ---------- Add SameSite cookies to FrameWorkBundle | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes, and added to changelog https://github.com/symfony/symfony/pull/28168/files#diff-276f5b13978c2ce3f555b9603f44801aR21 | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #27631 | License | MIT | Doc PR | symfony/symfony-docs#10202 Uses `session.cookie_samesite` for PHP >= 7.3. For PHP < 7.3 it first does a session_start(), find the emitted header, changes it, and emits it again with the value for SameSite added. I also tried it in a minimal Symfony 4.1 app, and works there too:  Commits ------- 4091feb Add SameSite cookies to FrameWorkBundle
Recipe update in symfony/recipes#452 |
@rpkamp I just found that symfony/src/Symfony/Component/HttpKernel/EventListener/AbstractTestSessionListener.php Line 75 in 8ab7077
|
It would be quite easy to make it compatible with PHP 7.3 as the value for samesite will be returned from the Note that this also means that currently the Or maybe all of this isn't really an issue as test drivers probably don't know how to handle samesite anyway, and if they do almost nobody is doing cross site testing? It certainly would be the edgiest of edge cases if the lack of samesite in that class would cause a false positive in tests for someone IMHO. |
What about passing the session options as a constructor argument to |
No, because |
Any chance this will be posted to Symfony 3.x? If not, any workaround? |
I realise this is a merged issue.. but 3.4 LTS is still considered a maintained branch.. SameSite is now a requirement.
|
we default to samesite=lax now, don't we? |
@nicolas-grekas thanks. My project is on Symfony 3.4 LTS This
results in
Will open a new issue now. |
… in session cookies (fabpot) This PR was merged into the 3.4 branch. Discussion ---------- [HttpFoundation][FrameworkBundle] fix support for samesite in session cookies | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #35520 | License | MIT | Doc PR | - This PR cherry-picks #28168 on 3.4, with a rationale given by @ConneXNL in #35520 (comment): > I hope I am wrong but I see the impact of not making any changes to Symfony 3.4 will have a tons of sites break if we cannot set the cookie's samesite setting (in the framework session and remember me) before Chrome pushes this update. > > Very soon all existing cookies are no longer going to work with cross-domains if you do not specify 'None' for the cookie_samesite. All external APIs that use cookies and are running SF 3.4 will break and devs will have no quick solution to fix their auth process. > > If you are using PHP 7.4, yes you can most likely use ini_set to workaround this issue. > > However, ini_set('cookie_samesite') does not work in PHP Version <= 7.2. I am not even sure PHP 7.3 supports the value 'None' as php.watch/articles/PHP-Samesite-cookies says it has support for 'Lax' and 'Scrict'. > > This effectively means SF 3.4 on PHP 7.2 (or PHP 7.3) is no longer supported for cross domain APIs with cookies. People would have to either update PHP to 7.4 (if they even can?) or go to Symfony 4 (with a dead live site is going to be a complete disaster). > > Since the impact of the change that chrome is about to roll out is so fundamentally changing our way to set cookies, I consider configuring samesite configuration in the framework an absolute requirement, not a feature, especially since SF 3.4 is still supported. > > What am i missing? > > Note: SF3 HTTPFoundation already supports the new cookie settings, it's just the framework that doesn't support it. Our BC policy embeds the promise that one should be able to keep the same app on a newest infrastructure (eg that's why supporting a PHP version is a bug fix). I think we can consider this for browsers here also. WDYT? Commits ------- f46e6cb [HttpFoundation][FrameworkBundle] fix support for samesite in session cookies
Uses
session.cookie_samesite
for PHP >= 7.3. For PHP < 7.3 it firstdoes a session_start(), find the emitted header, changes it, and emits
it again with the value for SameSite added.
I also tried it in a minimal Symfony 4.1 app, and works there too: