-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fixes setcookie to only send one Set-Cookie header per name #795
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
I'm not sure as to whether prevent the second set-cookie from being added, or just put a single warning. Right now, it just puts a warning, but the message is wrong (should be "should not"). I'm not sure what my future commit should be: fix the behavior, or fix the comment? |
Fixes? I fail to see how this fixes the problem, it only throws E_WARNING, but does not fix anything. |
Actually, I would expect it to be "last one wins", so the latter cookie overrides previous ones. It is IIRC also how browsers handle cookies with same name. |
It does not fix anything because the PHP code does not have a bug. The RFC does not forbid sending multiple cookies with the same name:
Although it is definitely not recommended, the RFC does not say MUST NOT. Even if it was forbidden by the RFC, it is not PHP's job to enforce conformance to RFCs. That is up to the programmer using PHP to not send improper data. The idea of having setcookie magically modifying the cookie header that is sent is nuts. It would be adding more magic behaviour to PHP to cover up the fact that the programmer who is calling setcookie() has a bug in their code. Adding a warning is enough. It tells people that they are doing something that is probably unwise, but is technically allowed. Doing anything more is not only the wrong thing to do anyway, but introduces a backwards compatibility break for no good reason. If someone is unaware they are calling setcookie() twice with the same name, the error will alert them and allow them to fix their code. If someone is deliberately calling setcookie() twice with the same name (e.g. for a legacy application that knows how to handle multiple cookies with the same name) then they will need to continue using the current behaviour. Changing the behaviour just because you think it would be 'better' is not a good idea. There needs to be a clear reason for changing the behaviour, and I don't think there is one here. |
@@ -91,6 +91,13 @@ PHPAPI int php_setcookie(char *name, int name_len, char *value, int value_len, t | |||
return FAILURE; | |||
} | |||
|
|||
if (zend_hash_exists(SG(cookies), name, name_len) == 1) { | |||
zend_error( E_WARNING, "setcookie() cannot be used twice with the same name" ); |
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.
"Cannot" is misleading (implies header wasn't set). This should say "should not".
Could you add a test case that triggers the warning? |
I've added 2 test cases: one with |
Why bother having a display_errors=0 test? We already test that display_errors works in other places. |
@TazeTSchnitzel to make sure the behavior of having 2 set-cookie headers is kept intact. When display_errors is set to on, you get the warning, so the 2nd header can't be set. (because headers are already sent.) |
@ralt Wait, what? Why does changing that ini setting affect behaviour?! |
@TazeTSchnitzel because when the warnings are shown, the headers can't be sent anymore after that. |
@ralt Oh, right, I see what you're saying. Because the warning was outputted, setting a header would fail. |
Exactly! That was hard to say, my bad. |
I've fixed similar issue in session module before. IIRC, I didn't use hash to detect already sent header to minimize changes. It may be better to rewrite session module to use the hash if this is going to be merged. Anyway, isn't it better to overwrite old setcookie() silently? It would be the programmer's intention almost always. |
@yohgaki w.r.t. the behavior, see #795 (comment) |
A couple of comments:
|
|
WS must be done in separate commits, ease merges and co (3rd parties too)
|
Doc ref is for ext and co, provides links to the doc too (not sure about Ze)
|
I understand it valid to send multiple cookies even if there might be different behavior browser to browser. There was similar issue when browser is sending cookies to server. There could be multiple cookies for a single URL and old browsers sent multiple cookies. Current browsers only send outstanding cookie for the browser to avoid confusion. (I have different opinion for this behavior, though) PHP could be behave like this for the same name and the same attribute cookies. Allowing to send multiple cookies is better to be optional, since latter one is the outstanding cookie almost always. The behavior depends on browser implementation. It's better to remove uncertainty as much as possible, IMHO. BTW, the patch should deal with "attributes" also. The same name cookie is completely valid if attribute differs and should not raise any error. |
@pierrejoye I've fixed the patch to remove whitespaces and to use |
According to the IETF RFC 6265, only one Set-Cookie header should be sent per cookie name. Fixes #67736
@jpauli fixed your comments. |
I agree with @Majkl578, it should adopt „the latter the winner“. But also agree with @yohgaki that it could be optional, dealing with changed behavior. That's my opinion. :-) @TazeTSchnitzel, @ralt: but you can also get both generated warnings in test, using output buffering functions, can't you? Like below: --TEST--
testing multiple setcookie() calls with the same
name (intentionally with display_errors On)
--INI--
display_errors=1
--FILE--
<?php
ob_start();
setcookie('foo', 'bar');
// called with the same name
setcookie('foo', 'second call');
setcookie('foo', 'third call');
ob_end_flush();
?>
--EXPECTF--
Warning: setcookie(): should not be used twice with the same name in %s
Warning: setcookie(): should not be used twice with the same name in %s |
Just an additional note for this patch. I have code like for all combination of $path, $domain, $secure, $httponly to remove unwanted cookies set by someone else. There should be a way to allow this kind of code w/o any errors. |
@yohgaki perhaps we could add |
@TazeTSchnitzel Yup , that would be the solution. Also, the Warning error message for setcookie() with the same cookie is somehow discutable. |
I mean, using |
Adding <?php
function _setCookie(...$attrs) {
unsetcookie($attrs[0]);
return setcookie(...$attrs);
} to enwrap trivially expected „the latter the winner“ behavior and beginners, inspired in that many tutorials, will start using non-breaking unsetcookie('foo');
setcookie('foo', 'new value'); calls wherever they will be „setting a cookie“ because of expecting supposed behavior which will be disapproved by that tutorials. And their codes natively using cookies heavily will grow up becasue of using two calls instead of one trivial. Do you want to accept this? But it could be good idea to adding |
Perhaps it would be better if instead of sending E_WARNING and not setting to have it issue E_NOTICE and override the previous cookie data. That way it will behave as expected if your intention is to update a cookie and at the same time provide a log trace in case it's overridden somewhere you don't want it to be. |
@coladict If we emit an E_NOTICE and do so after setting the cookie, it wouldn't prevent setting the header, yeah. |
@TazeTSchnitzel only if it is the last |
@ralt Typically, E_NOTICE is hidden in production, so it wouldn't stop sending further headers, right? |
Well, |
@TazeTSchnitzel unsetcookie() would be nice to have. Having '' value is hacky and we may try to remove as much as it can when an option is specified. e.g. combinations of domain, path, attributes(httponly, secure) Trying to remove all cookies is the best effort, since there is no way to remove all cookies for a name. It's much better than nothing. |
session_regenerate_id(true) deletes the old session file and submits a new session cookie with the new session id, but it doesn't overwrite the old session values and the 'Set-Cookie' header is sent twice (possible fix in PHP 5.6 -> php/php-src#795) In GET requests that perform login, the previous session needs to be destroyed before calling session_regenerate_id(true) to fix this problem Refs #7754
Adds arRestApiPlugin: - Used to fetch LOD from Archivematica for DIP uploads to AtoM - Only with browse taxonomies endpoint - Not enabled by default but available in the plugin list - With authoritation fix for Apache - Fixed Set-Cookie response header Adds hierarchical DIP upload method: - Used if a logical structMap exists in the METS file Changes non-hieararchical DIP upload method: - It now uses the fileGrp element to create the IO instead of looking at the objects folder - The IO are now saved only once (in both process) - Helper function moved to Qubit class Changes metsData ES mapping: - Adds Mediainfo tracks and other data that was added for the DRMC project Adds QubitMetsParser: - Moved most of the METS related code in the DIP upload process to it - Used to get the data needed from the METS file for the IO search index - Code clean-ups in the ES related code - Allow METS with and without the namespace changes Squashed commit of the following: commit 68f8f19 Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Sat Jan 31 01:57:50 2015 +0100 Fix Set-Cookie header in arRestApiPlugin responses session_regenerate_id(true) deletes the old session file and submits a new session cookie with the new session id, but it doesn't overwrite the old session values and the 'Set-Cookie' header is sent twice (possible fix in PHP 5.6 -> php/php-src#795) In GET requests that perform login, the previous session needs to be destroyed before calling session_regenerate_id(true) to fix this problem Refs #7754 commit 249f956 Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Wed Jan 28 22:23:01 2015 +0100 Fix uploads paths, refs #7754 commit f1641ce Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Mon Jan 26 21:28:52 2015 +0100 Remove exiftoolRawOutput field from ES, refs #7823 commit d3a37cb Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Fri Jan 16 07:03:38 2015 +0100 Improve DIP upload code, refs #7754 - Move most of the code related to METS to QubitMetsParser - Move helper function to Qubit class - Change non-hierarchical method to use fileGrp (USE="Original") instead of going over the objects folder - Use mappings to find dmdSec for the files (more secure looking at fileId) - Avoid saving IO three times - Use 'p' instead of 's' for premis namespace" commit 669a5e0 Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Wed Jan 14 19:56:11 2015 +0100 Fix Mediainfo queries, refs #7754 - Workaround to allow old and new METS files from Archivematica after the namespace changes commit 9bc65da Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Tue Jan 13 22:05:34 2015 +0100 Changes in METS data for ES, refs #7754 - Adds Mediainfo tracks and some other data from DRMC - Moves METS parser code to QubitMetsParser class - Code clean-ups and cosmetic changes commit bceb6f1 Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Wed Aug 20 19:51:54 2014 +0200 Change DIP upload process from logical structMap - divs without TYPE are not added and their children added to its parent - LOD from div with LABEL='objects' added to target info object - Creates UUID mapping for files under fileGrp with USE='original' - Only adds files wich UUID is found in the objects folder commit e7d6ff5 Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Wed Aug 20 18:06:27 2014 +0200 Check main DMD section only in physical structMap commit 5b599e9 Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Wed Aug 20 18:01:05 2014 +0200 Create file ID to UUID mapping commit 179bf9f Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Wed Aug 20 17:20:54 2014 +0200 Allows 'div' and 'mets:div' in logical structMap commit bca55cc Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Wed Aug 13 15:06:00 2014 +0200 Fix LOD mapping if no TYPE attr, fixes #7098 commit 4128330 Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Thu Aug 7 14:43:47 2014 +0200 Allow enable/disable arRestApiPlugin, refs #6863 commit bf53d50 Author: Justin Simpson <jsimpson@artefactual.com> Date: Wed Aug 6 23:09:41 2014 -0700 enabling arRestApiPlugin refs #6954 arRestApiPlugin must be enabled in order for api urls to work commit 27e4127 Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Thu Jul 31 04:16:44 2014 +0200 Add hierarchical DIP upload If a structMap type logical is included in the METS file the information objects are created following the parent-child div hierarchy and digital objects are added to those divs with a fptr tag as child A level of description is added to the info objects from the type attribute inside the div. If one of those levels doesn't exit in AtoM the upload process is stopped Fixes #6954 commit e1e580a Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Sat Jul 5 02:12:58 2014 +0200 Changes in browse taxonomy endpoint - Remove id from results - Allow culture as a param - Return note and name in requested culture or the default culture with culture fallback - Return only not empty fields commit 870d670 Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Sat Jul 5 01:18:30 2014 +0200 Modify comment commit 948340a Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Sat Jul 5 01:16:55 2014 +0200 Fix API authorization in Apache commit 42d324a Author: José Raddaoui Marín <raddaouimarin@gmail.com> Date: Fri Jul 4 19:28:41 2014 +0200 Add arRestApiPlugin Only with the browse taxonomies endpoint
This appears to be duplicated by #849 |
@Danack Look more carefully. One targets 5.6, the other master. |
RFC compliance is not good always. For example, I have code that try to remove all offending cookies. It is used to prevent session fixation via adoptive session or DoS. (I described in previous reply) //Remove all cookies as many as possible. i.e. Try all combinations of $path, $domain, $secure , $httponly. Actual code is omitted http://tools.ietf.org/html/rfc6265#page-8 It say server SHOULD NOT. However, algorithm described in Section 5.2 does not ignore multiple cookies of the same name. I don't object this if I can remove all cookies of the same name by single API. Suggestion:
We should consider how these could be used with session. This code will produce multiple set-cookie headers of the same name. unsetcookie() and session_start() should not be called at once to avoid race condition. unsetcookie() should be called from a dedicated request. This applies to setcookie() also. e.g. This should not be done as well. This should be in the document otherwise there may be number of bug reports complaining "setcookie() cannot send cookie and raise error". unsetcookie() should accept these options
Please note that unsetcookie() must try all domain/path combinations. Anyway, even when PHP forbid multiple cookies of the same name, we may use header() to send multiple set-cookie headers. I don't object as long as this is targeted for 7.0. I think I've removed multiple set-cookie headers from session module, but there may be leftover. Please test this patch with session extensively. |
Comment on behalf of krakjoe at php.net: Since this PR targets a security fix only branch of PHP, I'm closing the PR. |
According to the IETF RFC 6265, only one Set-Cookie header should be sent per cookie name.
Fixes https://bugs.php.net/bug.php?id=67736