Closed Bug 11630 Opened 25 years ago Closed 25 years ago

[BLOCKER] cookie problem - unable to set multiple cookies in one response

Categories

(Core :: Networking, defect, P3)

defect

Tracking

()

VERIFIED FIXED

People

(Reporter: hshaw, Assigned: jud)

References

()

Details

The following needs to be fixed to enable e-commerce sites.

The parsing code in

mozilla/extensions/cookie/nsCookie.cpp:cookie_SetCookieString()

assumes only one cookie will be set.

During login/logout, the CDW site sets 4 or more cookies, whose
HTTP headers look like

  Set-Cookie: USER%5FNAME=HSHAW; path=/
  Set-Cookie: ID=2Q7P7Q8X; expires=Wed, 01-Jan-2020 06:00:00 GMT; path=/
  Set-Cookie: company=01; path=/
  Set-Cookie: program=0; path=/

By the time this information gets passed to cookie_SetCookieString()
it looks like

  USER%5FNAME=HSHAW; path=/, ID=2Q7P7Q8X; expires=Wed, 01-Jan-2020 06:00:00
GMT; path=/, company=01; path=/, program=0; path=/

The parsing code in cookie_SetCookieString() will only recognize the
first cookie value, in this case, USER%5FNAME=HSHAW

The parsing code needs to be modified to recognize a comman separated
list of cookies.

As a side note, the comma separated list that necko is passing to the
cookie code is valid cookie syntax per RFC 2109

http://andrew2.andrew.cmu.edu/rfc/rfc2109.html

- Hubie
Summary: cookie problem - unable to set multiple cookies in one request → cookie problem - unable to set multiple cookies in one response
Do I conclude that in this case netlib was making four calls to
cookie_SetCookieString whereas in necko you have changed this to be one
concatenated call instead?
Am I correct in saying that this is for http cookies only -- javascript cookies
will never need this parsing.  In that case the additional parsing should not be
done in cookie_SetCookieString (which is already cumbersome enough) but rather
in the public routine COOKIE_SetCookieStringFromHttp.  That public routine is
currently doing some pre-processing and then calling the local
cookie_SetCookieString routine.  Right now the public routine is calling the
local routine once, but under this change it should call it once for each of the
comma-separated strings.

Furthermore, the public routine itself is already doing some processing of the
fields (the expire field in particular), so this further necessitates that the
change be done in the public routine and not in the private one.
Steve, Set-Cookie headers can contain multiple cookies. It's conceivable that
js cookie setting could do the same (if the api permits it). I think this
parsing change should occur in the core parsing code.
Judson is right, it could have came directly from the server in comma separated
form in one 'Set-Cookie' header as opposed to coming from Necko.  If there is a
way to set multiple cookies at once in Javascript, then it needs to be in a
routine used by both http and javascript, unless Javascript internally
serialized the cookie setting and calls you multiple times, once for each
cookie.
If what you are saying is correct, then this bug existed in our browser from day
1 and is a problem in our current 4.x browser as well.  Is that correct?  Can
you give me the URL of a site that sends back the cookies in such a format?
Does the 4.x browser handle that site correctly?
Off the top of my head I don't have a site which does this from the server
side, but I do remember writing server side code that did it this way
(multiple cookies for a single Set-Cookie header) back in the netscape 1.0
days.

If you look at section 4.2.2 in the cookie RFC, it
mentions that a comma separated list of cookies is
valid syntax.

http://andrew2.andrew.cmu.edu/rfc/rfc2109.html

It's possible that 4.x doesn't process this correctly,
but I haven't checked.  You can setup a test case using
the data I provided and see how 4.x behaves.
The point I'm trying to make is that if this is the way things always worked,
then this bug is of very low priority.  Upon first reading of this bug report,
it appeared as though this was a change that necko was making in that it batched
up several cookie requests and sent them all in one string to the cookie module.
In that case this would have been a high priority bug and possiblhy an M9
showstoper.  But now I realize that this is not the case and it is very low
priority.
Status: NEW → ASSIGNED
Target Milestone: M12
Setting target milestone to M12
> Upon first reading of this bug report, it appeared as though this was
> a change that necko was making in that it batched up several cookie
> requests and sent them all in one string to the cookie module.

This is exactly what happens (necko batches).  We were just pointing out
that, in addition, even if necko didn't do it, it could naturally occur.

I didn't look at what netlib used to do, so I can't comment on whether
the batching is a necko innovation or not.

> In that case this would have been a high priority bug and possiblhy
> an M9 showstoper.

I think it's more important than M12.  Hofmann and you guys are a
better judge of whether it's M9 material.

- Hubie
OK, I think I have a trivial fix for this problem.  But I don't have any test
cases to try it on.  Could one of you generate a URL that I can use for testing.

Also, if someone would like to code-review the change, I'd appreciate it.
Here's the code that I will add to the head of COOKIE_SetCookieStringFromHttp.
Note that I simply have the routine call on itself recursively.

  /* allow for multiple cookies separated by commas */
  char *comma = PL_strchr(setCookieHeader, ',');
  if(comma) {
    *comma = '\0';
    COOKIE_SetCookieStringFromHttp(curURL, setCookieHeader, server_date);
    *comma = ',';
    setCookieHeader = comma+1;
  }
Oops, let's try again.  Here's a revised change.

 /* allow for multiple cookies separated by commas */
  char *comma = PL_strchr(setCookieHeader, ',');
  if(comma) {
    *comma = '\0';
    COOKIE_SetCookieStringFromHttp(curURL, setCookieHeader, server_date);
    *comma = ',';
    COOKIE_SetCookieStringFromHttp(curURL, comma+1, server_date);
    return;
  }
The rfc 2109 says that the Set-Cookie response header comprises the token Set-
Cookie:, followed by a comma-separated list of one or more cookies. But the
expired attribute could also have a a comma in its value. for e.g.
Set-Cookie: ID=2Q7P7Q8X; expires=Wed, 01-Jan-2020 06:00:00 GMT; path=/.
We need to handle this case too.
Now that's interesting.  The latest cookie spec doesn't even have an expires
atrribute.  Instead it has a max-age attribute which simply takes a numeric
value (number of seconds) and so contains no commas.

So which spec are we implementing to?
> 10.1.2 Expires and Max-Age
>
> Netscape's original proposal defined an Expires header that took a
> date value in a fixed-length variant format in place of Max-Age:
>
>   Wdy, DD-Mon-YY HH:MM:SS GMT
>
> Note that the Expires date format contains embedded spaces, and that
> "old" cookies did not have quotes around values. Clients that implement
> to this specification should be aware of "old" cookies and Expires.

There is a note regarding the Expires header.

IMO the important thing is cookies that are in use today and that
necko is passing to the cookie module work.  That's what we should
concentrate on right now.

Bringing the cookie parsing up to spec is not as important right
now, especially if nobody (or very few people) use the new syntax.

Neeti is right, solely searching for comma is not a good way
to separate the different cookies.  Unfortunately due to the
way the original cookie spec was designed, parsing comma
separated cookies is not as easy as you'd believe.

It's significantly easier to process the cookies before they
get merged together.
After a lengthy discussion about this with Hubie, I have now come to the
conclusion that the cookie module should not be changed and that necko should
not batch cookies up to be passed as a single string.  Let me give my
justification.

First, I agree that we are not compliance with the cookie spec which says that
sites can send a concatenated cookie.  But that's how the 4.x browser works and
nobody has complained about it -- so apparently there is no site for which we
fail (if there was, the site probably dropped the concatenated cookie.

Second, if we make the change to our cookie parsing, then we would break a site
that sends a cookie having the name A and the value "B, C=D".  In other words,
the cookie string would be of the form "A=B, C=D".  Can a site currently send
such a cookie, even though the spec says that space and comma would need to be
quoted in order to appear within a cookie value?  The answer is yes -- Hubie and
I looked at our own cookie files and he found examples of commas in values and I
found examples of spaces (and the space examples came from mozilla.org).  So
currently a site could be sending out comma-space in the cookie value and we
would be intepreting it as the site intended.  By changing the cookie parsing
now, we would break that site.

I've already gone through a year-long exersize of fixing the cookie
implementation so that the host a.b.co.nz cannot set a cookie for .co.nz
(because that was in violation of the cookie spec).  The downside was that we
broke yahoo.mail who indeed was violating the cookie spec and consequently we
had to back out the change.  I'd hate to repeat this exersize again.  The safest
solution is for necko to change and not concatenate cookies.
Well, although I've already given arguments as to why we shouldn't change the
cookie module, here is a cleaner version of the code that I would use if we
decide to do otherwise.  It's yet another revision of my patch above but uses a
while loop so that the level of recursion is never greater than one.  Of course
the test for comma would have to be more complicated to reject for the comma
within the expires field, as Neeti pointed out.

  /* allow for multiple cookies separated by commas */
  char *comma;
  while if(comma = PL_strchr(setCookieHeader, ',')) {
    *comma = '\0';
    COOKIE_SetCookieStringFromHttp(curURL, setCookieHeader, server_date);
    *comma = ',';
    setCookieHeader = comma+1;
  }
Assignee: neeti → valeski
Status: ASSIGNED → NEW
Since nobody from necko has given arguments agains fixing this in necko rather
than in cookie code, I'm assigning this bug to necko team and changing the
component to necko.  Basically necko should not be concatening cookie strings.
Assignee: valeski → morse
see my previous comments
Component: Cookies → Necko
Target Milestone: M12 → M9
Judson, did you see my comments?  How do you propose we change the cookie code
and not break existing sites?  The only safe solution that I can think of is not
to accept concatenated cookies, just as we always have been doing.  Or

I don't want to play football with this bug and pass it back and forth.
So please present a safe counter-proposal if you can, and I will gladly
implement it.  Otherwise please change necko.
Guys is this M9 material. Looks like M10'able.
Does necko always batch cookie requests for sites that set more than one
cookie? If so, then until this bug is fixed we will break every site that sets
more than one cookie.  How can we ship M9 with such a bug.  Or am I
misunderstanding something here?
Yes, currently necko passes multiple set-cookie request headers as a
single ',' separated string of multiple cookies.  Any site that returns
more than one Set-Cookie header will have difficulty getting the cookies
set.

With the assumptions of the current cookie parsing code, a-v pairs like
'expires=' and 'domain=' could be associated with the wrong cookie variable.

This makes shopping at many sites somewhat difficult.
*** Bug 12005 has been marked as a duplicate of this bug. ***
Assignee: morse → dp
I got the overall picture here.

Necko is putting together multiple cookie headers into a single one because it
makes life simpler for it. Is that true. If so, fine. The cookie module can un
strip each one.

But shouldn't necko be quoting the individual headers to protect for the
separator that *necko* is adding.

If we agree to all the above, then the fix would look like:
Cookie module: unquote and strip individual cookie headers
Necko: quote when putting together multiple headers
There are two cookie specs: The netscape cookie spec (the only one currently
implemented (and only partially implemented at that)), and 2109. The only 2109
implementations are test beds. Although we should be ready for 2109 as it fixes
some important holes in the old netscape cookie spec, we need to make sure we
don't break the world with a change in our cookie parsing.

Our original (4.x-) cookie implementation couldn't handle multiple cookies in a
single set-cookie header. Subsequently we weren't coded to "spec".

Necko condenses multiple headers into a single header using HTTP header
condensing rules and delimiter tokens. We did this not for ease of coding in
necko, but to follow http header parsing more closely.

I know how dirty the cookie setting code is, but we should fix this there. We
wouldn't be breaking existing functionality, and we'd be providing a cookie
implementation that is more up to date.

I'm not clear on the problem w/ handling the comma seperated list. We should
never hit the expires/max-age because the list is separated from the attributes
by the ';' token.
Jud could you give parsing logic for a simplified form of hubie's example:

  Set-Cookie: USER%5FNAME=HSHAW; path=/
  Set-Cookie: ID=2Q7P7Q8X; expires=Wed, 01-Jan-2020 06:00:00 GMT; path=/
  Set-Cookie: company=01; path=/

USER%5FNAME=HSHAW; path=/, ID=2Q7P7Q8X; expires=Wed, 01-Jan-2020 06:00:00 GMT;
path=/, company=01; path=/

When would the parsing logic look for ; and when for ,  If the cookie module
knew that from the above, it would be glad to implement it. The best I can come
up with is the largest pattern that matches [^=,;]+=[^=]+[,;]

One premise I am running with is breaking old behaviour would be bad. I dont
know if , in the expires is valid per spec or not. But if 4.6 supported it, we
should try our best to.
Steve mentioned this somewhere above...

The difficulty with parsing based on ',' is that the value a cookie
may have, includes space and comma as valid characters.

By examining our cookie file it appears that sites use space and ','
in their cookie values without quoting them as the current spec mandates.

Without quoting the cookie value it is impossible to deterministically
tell whether the ',' is part of the cookie value, or a separator between
multiple cookies.

With some heuristics and assumptions, we could probably get away with
very good approximation of the old parsing code, but there are cases
(possibly artificial?) where it is still ambiguous.

If sites quoted there cookie values, there wouldn't be any ambiguity
problem, but most sites don't do this (at least that's the appearance
based on our cookie file)

Also, to address DP, the current cookie spec allows for servers to
set multiple cookies in a single header by separating them by commas.
They could naturally occur in this form even if necko did something
different.  The counter-argument is most sites don't seem to be setting
cookies this way.

The comma is valid in the expires field, but that shouldn't be
a problem for parsing, just makes it more complicated.  The main
problem is the cookie value isn't usually quoted, and a comma
in the cookie value would be impossible to distinguish from a
multiple cookie separator unless you use heuristics and make
assumptions

So, either solve it with cookie parsing or solve it by changing
what necko passes on, but do it some way, because setting multiple
cookies is essential for lots of sites where you login, including
e-commerce.

- Hubie
Don't talk about the expires field, that's not the issue.  Hubie just explained
it correctly.  The problem is in the cookie value itself.  See my example above.
Jud, do you understand the issues. Would you prefer to talk this out. Let me
know.

If necko gave every cookie header separately, then we can retain the old parsing
code in the cookie module. There is a lot of value to doing that. We pretty much
ASSURE backward compatibility. Fixing multiple cookies in a header would be a
feature that the cookie module could then implement.
dp, I agree with everything you just said except for the last sentence.  How can
the cookie module add this as a feature?  To do so would break backwards
compatibility.

It's the same as the issue with a.b.co.nz setting cookies for the .co.nz domain.
This is in violation of the cookie spec, but to fix it in the browser will (and
did) break backwards compatibility and we were forced to back it out.
Steve i hear you. let us postpone that discussion to later. I assure you that
our priorities will be:

1. Maintain backwards compatibility
2. Implement additional cookie features

I want to get point (1) fixed for M9. We are broken now.
Just to clarify, in case this is still not clear.  Judson wrote above:

   "I know how dirty the cookie setting code is, but we should fix this there."

The issue has nothing at all to do with how clean or dirty the cookie code is.
In fact, the patch would be very clean, as I described above.  THIS IS NOT THE
ISSUE!!!

  "We wouldn't be breaking existing functionality,"

We absolutely would.  We would break any site that is currently violating the
cookie spec and setting a cookie named "A" to have the value "B, C=D".

  "and we'd be providing a cookie implementation that is more up to date."

We have already declined from making our implementation be "more up to date" in
regard to domain cookies because it broke backwards compatibility.  This will do
the same.
Assignee: dp → valeski
I talked to jud. We decided the safest and surest way to fix this would be to
not condense cookie headers.

Jud was going to talk talk to gagan and rpotts to take care of this.
Reassigning. (thanks jud).

I think we got to fix this for M9.
*** Bug 12093 has been marked as a duplicate of this bug. ***
*** Bug 12093 has been marked as a duplicate of this bug. ***
*** Bug 12093 has been marked as a duplicate of this bug. ***
*** Bug 12093 has been marked as a duplicate of this bug. ***
*** Bug 12093 has been marked as a duplicate of this bug. ***
*** Bug 12093 has been marked as a duplicate of this bug. ***
Summary: cookie problem - unable to set multiple cookies in one response → [BLOCKER] cookie problem - unable to set multiple cookies in one response
Severity: normal → blocker
"QUIT MESSING AROUND AND FIX THIS SPANKY" - (tm) Jevering
Status: NEW → RESOLVED
Closed: 25 years ago
Resolution: --- → FIXED
fix checked in 8/20/99, 11:38am pac time, into SeaMonkey_M9_BRANCH.

I made two changes:
1. I checked in Steve's code to recursively set cookies parsed out of a new line
delimited cookie list.
2. HTTP still condenses the cookie headers, but I've special cased (this really
sucks) "set-cookie" so that it uses the new line char when appending/condensing
multiple set-cookie headers.
Jud, I haven't seen your changes, but from your description, it sounds like it
should work.  Of course if you took my code as presented in the bug report, I
presume you fixed up the error that I just noticed -- I had "while if(" when of
course I meant just "while(".
Status: RESOLVED → VERIFIED
I verified the basic functionality of this using bugzilla, which sets both the
login name and password as a cookie in one response. It now works on the M9
branch, all platforms, though continues to be broken on M10 branch, which is to
be expected. Marking verified.
Bulk move of all Necko (to be deleted component) bugs to new Networking

component.
You need to log in before you can comment on or make changes to this bug.