-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
net/http: add Cookie.Valid method #46370
Comments
CC @neild |
This seems reasonable, especially since Perhaps something like:
|
See related comment #37055 (comment). |
Yeah I can't say i'm a fan of the current behavior of I know we're in a development freeze right now but if you think this is a worthwhile change I can draft up a PR to be considered for the next release cycle.
Thanks for the context! I don't expect the change i'm proposing to expose what exact cookie values are valid/invalid. This would only be providing a method to tell whether or not the cookie as a whole is "valid" by Go standards, the specifics of which may or may not change in the future (as @vdobler pointed out). If anything, I think having an |
What is the exact meaning of "valid" for a cookie? Is it valid if it is not discarded by the |
My thought was to consider a cookie "valid" if no values are discarded by the More specifically, Edit: Clarifying as there was some confusion here: The above is just an example and additional validation cases may be considered. As for what constitutes a "valid" cookie, I think the definition "a cookie is valid if no values are discarded by the Any additional formatting done to the values like adding quotes etc. shouldn't make a cookie invalid, as the resulting cookie header has all the same data. On the other hand, I would consider a cookie with a malformed domain or expiration invalid, because after calling |
Could cookies with a value or path that is changed or completely discarded by the &http.Cookie{Name: "c", Domain: "domain.com", Expires: time.Now(), Value: ";"} but the value would be discarded by the
https://play.golang.org/p/Lm-6VjMFPV7 I'm not saying the above definition is wrong, but an |
Under the definition "a cookie is valid if no values are discarded by |
Yes if the value is discarded and an error logged, I would consider that cookie invalid |
You are right, I had considered this as a definition
|
Yeah sorry, let me edit that comment to be more clear. I think a simple definition of "a cookie is valid if no values are discarded by (http.Cookie).String" sufficiently describes what we're trying to achieve here. |
As an alternative to the // Serialize works like String, but returns an empty string and a not nil
// error instead of discarding some fields and logging the error as String
// does.
func (c *Cookie) Serialize() (string, error) It can be used like this v, err := cookie.Serialize()
if err != nil {
// do somenthing with the error
}
w.Header().Add("Set-Cookie", v) |
I am not sure I understand the reason for this, especially if you control the parameters of the cookie yourself: Just do not set invalid names or values. Being able to validate something is important in all the cases you take data you do not control and have to process it. But there is no real need to validate data you generate. Cookies you receive (at least via the builtin cookie stuff from net/http) are valid and can be sent in Cookie and Set-Cookie headers. The rules for proper cookie names and values are pretty trivial and well understood and documented in RFC6265. Using something else is a simple programming (or design) error and not some actionable code path. How would somebody use a Cookie.IsValid method?
In my opinion there is no sensible way to react in code to a cookie whose IsValid method returns false. So there is no need for a IsValid method. Pointers can be nil and you can check if they are "valid" by a simple If your code generates name, values, domains, expire-times, etc. dynamically from data: Design these functions so that they do not generate "invalid" cookies. E.g. encode arbitrary values in base64/92/whatever. Names of cookies are part of their identity and need to be agreed on upfront in the design phase of your application, so just use valid names. Conclusion: There is no need for a |
I totally agree that invalid cookies should not be created a priori, but the documentation of the I agree that We could make sure that the cookies comply with the specifications but we may want to do a second security check. For example: v, err := cookie.Serialize()
if err != nil {
log.Printf("attempt to send an invalid cookie: %s", err)
http.Error(w, http.StatusInternalServerError, 500)
return
}
w.Header().Add("Set-Cookie", v) We can also use the |
Any valid RFC 6265 cookie is not invalid and that's all to know. "Use valid cookies" instead of "do not use invalid cookies". It is that simple.
Again, there is: Any valid cookie is valid and you know that. What counts as valid is defined by RFC 6265 not by the String method. The opposite case i.e. which cookies String considers invalid is a) not interesting, b) must not happen anyway, c) is un-actionable and d) is guaranteed to be an invalid cookie according to RFC 6265. The reason why the String method doesn't consider all "RFC-6265-invalid" cookies as "Go-invalid" is to allow compatibility with broken legacy systems, frameworks and browsers. The reason why this isn't documented is because this compatibility with real-word browsers is a) important and b) a moving target. |
cc @ianlancetaylor as likely proposal |
|
It allows compatibility but not documenting the level of compatibility and not providing a function to check this compatibility, you can't rely on it to write Go code that must interact with a legacy system, framework or browser. |
I'm not sure what you want to express with
because as stated it is wrong: Any RFC-6265-valid cookie-value is considered valid by net/http.Cookie.String. Maybe you are misinterpreting
The compatibility guaranteed is "If you follow RFC 6265 your cookies will work.". That is guaranteed and you can rely on this. If you have to interact with a legacy system which need non-RFC-6265-compliant cookies you have to test your code or work with raw headers. |
I don't think so, the definition of cookie-value is
so
Apart from the first and last octet.
There is no reference to this statement in the RFC 6265.
In this case I don't understand why |
@vdobler i think the
but it is
|
@gazerro Again. The optional DQUOTEs around the cookie value in RFC 6265 are optional and are *not part" of the value. http.sanitizeCookieValue is correct. Your reading of the RFC is wrong. Sorry. I do not have time to look it up in the RFC and prove to you that the cookie value doesn't include the optional quotes. See e.g. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes which is pretty clear that " do not belong into a cookie value and the pair of " around a value are optional and not part of the value. |
I doubt that both proposed godoc comments for In
the "change any field" is at least misleading (if not wrong). The
Maybe "String does not discard or sanitize any fields" would be better. And
is almost a circular definition. "A cookie is valid iff Valid". A Go
Maybe "Valid reports whether the cookie is syntactically valid." would be better. |
I do not know which arguments where discussed in the proposal review meeting but I'm still not really convinced by the arguments in this issue that
For this use case a Or is there really a realistic case where the semantics of dynamically generated Domain, Path, Value, Expires, Secure/https are all okay, it just happens that sometimes Path contains an emoji or value contains newlines?
This can be achieved by faking a cookie roundtrip: Create a request, add your cookies, serialize the request, pars it again and check your cookies are there "unaltered". This is far less convenient than calling |
@gazerro Please excuse if I misread your comments #46370 (comment) and #46370 (comment) . I was under the impression that you think |
@vdobler I think we have two different and irreconcilable interpretations of what a "cookie value" is and what a "valid value" is for the RFC 6265 and the The new method should be used to check whether |
@gazerro Yes our views are different. I do share the view of bradfitz expressed in #18627 (comment), the view of the Mozilla developers expressed in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes and the view of RFC 2965 where these double quotes originate. You share the intended reading of RFC 6265 as clarified in RFC 6265bis-02 draft and what browser engines do. It seems that the semantic meaning of "value of a cookie" has changed in the past 15 years and that treating |
It seems like we keep coming back to the Go valid cookie vs. RFC 6265 valid cookie debate which, while I think is probably worth talking about, is not the issue this proposal is trying to solve. Whether or not it was the original intention, the All im proposing is a way to catch these "syntactically" invalid (according to
This wouldn't attempt to solve the problem of clients not accepting a cookie, as that is a whole separate issue. It should be the job of the client to report to the user when/if it rejects a cookie and why (for example, browser dev tools typically show you when a cookie was rejected). Really what it comes down to is that I think it's good to handle what cookie validation we can on the server side, as server side will naturally be easier to debug, but when there's no way to tell when the validation fails it doesn't exactly add as much value.
Sure, there are other ways to achieve this but like you said, they are more complex. If these cookie validation functions already exist in the stdlib, it seems an easy win to me to simply expose them under a new public method. |
This is the first very good argument (in the sense of convincing me ;-) in this thread. There are characters that are impossible to send (e.g. 0x0a or 0x0d) and stuff that might work (e.g. chars in the 0x80-0xff range and or UTF-8 encoded stuff). With Should there be a new field like
|
@vdobler I don't have strong feelings either way about adding a new |
The problem being solved here is that http.SetCookie silently throws away cookies it deems invalid. |
Based on the discussion above, this proposal seems like a likely accept. |
No change in consensus, so accepted. 🎉 |
I'd be happy to take a stab at implementing this, i'm pretty familiar with the cookie validation logic at this point. |
Change https://golang.org/cl/338590 mentions this issue: |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
I am attempting to construct an
http.Cookie
that will be set on anhttp.Server
's responseWhat did you expect to see?
Being that the http.SetCookie (or more specifically, the
cookie.String
method) function validates various fields of the cookie struct and returns no error, I would expect there to be a way to validate the cookie myself before attempting to set it.Unfortunately, these validator functions are all unexported and the only way I can see to achieve this today would be to re-write the validation logic in my own code, which comes with its own set of concerns.
I can see why generally there isn't always a need to validate a cookie yourself before writing, but when dealing with cookies containing lots of dynamic attribute data (e.g. an app implementing oauth/oidc flows) it becomes paramount that the cookies being returned are written as expected.
What did you see instead?
The
http.SetCookie
function will log to stderr (or not, depending on your logging setup) and silently discard the portion of the cookie that failed validation from the final header.For myself, this caused the
Domain
attribute to be dropped from the final cookie in the response. That in turn caused the cookie to not be sent in subsequent requests to a subdomain on the same host, which resulted in an error.Proposal
Cookies in
net/http
have a concept of validity, but what constitutes a "Go valid cookie" is deliberately left undocumented.Additionally, it is quite easy to construct an "invalid" cookie (by Go standards) and have it go unnoticed, due to the
(*http.Cookie).String()
method silently discarding any invalid fields.I propose adding a new utility function that allows one to programmatically check a cookie's validity under the same rules used by
net/http
.This can be either a validity check method, a serialization method, or both:
Use Cases
(*http.Cookie).String()
method just log to stderr and discard fields (which may be entirely swallowed depending on your logging setup) you'd be able check cookie validity beforehand, and handle invalid cookies gracefully by logging your own structured error, incrementing a failure metric, returning a different http response, etc.The text was updated successfully, but these errors were encountered: