Securing cookies in Go
calhoun.io
calhoun.io
Defaulting to HTTPS is not enough to prevent cookie theft by man in the middle attacks, because a browser will still happily do HTTP connections. (They'll get redirected to HTTPS if you configure the server for HTTPS only, but that's only happening after the Cookie is already transmitted without encryption.)
Traditionally there is a "secure" flag for cookies. (I know little go, but from reading the docs it seems it can be set in http.Cookie with Secure = true, similarly to the httponly flag he's describing.)
However these days there's a better solution: Use HSTS, which enforces HTTPS connections after the first connection has been made. Even better is HSTS preloading, where your domain is added to a whitelist of HSTS domains within common browsers.
[1] https://www.troyhunt.com/understanding-http-strict-transport...
I expect PHP5 to be that way. php7 and golang doing that, in this day and age, is inadmissible.
Yeah you should still be able to write a quick hack and make it work fine without worrying about security, but the language/framework should at least make you explicitly say "set this cookie, and allow all domains and client side access. fine".
document.cookie = 'name=value';on the server, the chance that your cookie is for auth is high, so make a secure cookie the defaul. force http headers that say server only, etc. Then allow for the security to be dropped via the extra arguments, not the other way around.
While I agree with the sentiment, domain is probably the worst possible example, because it defaults to the most specific possible configuration of "exactly the same domain".
- Setting Secure and HttpOnly attributes are exactly what I do by default in gorilla/csrf - you have to explicitly override these[1] - CSRF prevention without these is a losing battle. I'm positive many devs miss the docs though, wonder why the library isn't working on their local machine (without HTTPS), and move on to a library that doesn't have these safe defaults. This is a hard thing to fix: docs only go so far.
- securecookie just returns the authenticated, and optionally encrypted, cookie value. It's pretty minimal. I'm already looking at a v2 with some breaking API changes[2], including using an AEAD instead of the original authors' AES+CBC + HMAC construct. This could also be an opportunity to provide a convenience method - WriteCookie - that sets sane defaults for you, ala gorilla/csrf (which I wrote from scratch)
But as per the other comment: "all domains" is not correct. Cookies are still origin-scoped. HttpOnly is the real problem; followed by Secure. The SameSite attribute[3] is another lever to pull here: and, of course, for devs to learn & potentially disable.
[1]: https://godoc.org/github.com/gorilla/csrf#Secure [2]: https://github.com/gorilla/securecookie/issues/43 [3]: https://github.com/gorilla/sessions/releases/tag/v1.1.2
Note also the existence of github.com/chmike/securecookie which is as secured, and more efficient in size, number of allocations and speed.
https://golang.org/src/net/http/cookie.go?s=3875:3923#L147
func SetCookie(w ResponseWriter, cookie *Cookie) {
if v := cookie.String(); v != "" {
w.Header().Add("Set-Cookie", v)
}
}
If you want to set the header "Set-Cookie" to the empty string instead of not sending the header, you can just call w.Header().Add("set-cookie", "") yourself, I guess.If I as a client of this library call "SetCookie", and after the call, a cookie has not been set (and I was not notified), I would call that an error (or fault, or whatever), independent of implementation.
As SetCookie just adds a new header to an aliased map (Headers) and setting to a map shouldn't fail (how it could?), it makes sense to report any error when the HTTP response is written serialized to the socket, not before. At least, for me, as you can manipulate the response as you wish and you only need to worry about errors on write.
The point is, there is only so much validation of free-form text you can do. Ultimately your tests need to ensure that the headers you set do the right thing. And in the case of cookies, you are adding no appreciable overhead to automated tests by using httptest.Server and an actual client that understands cookies, so you'll find the bug pretty easily.
I agree that it would be nice to validate the cookie object and get some error message instead of an empty string. But it's not an error that you are ever going to take action on at runtime. Nobody writes:
cookie := GenerateInvalidCookie()
if _, err := cookie.Serialize(); err != nil {
log.Printf("ok, I guess we'll use a valid cookie")
cookie = GenerateValidCookie()
if _, err = cookie.Serialize(); err != nil {
log.Fatalf("you can't say I didn't try")
}
}
w.SetCookie(cookie)
So at some point, you have to ask what you're going to do about it. In the end, whether or not a cookie is valid depends on the user agent that is receiving the cookie. Test it against that.Mildly reminiscent of x/crypto/acme/autocert only having supported TLS-SNI because it made for more elegant code and the other methods didn't, but that's probably too cynical an interpretation.
what I do is set the domain to be specific to my subdomain. (note this really only works when doing subdomain stuff, if you're doing subroute things like Amazon ALB.. then I dunno)
Having things be separated like this is good, since Cookie's can get big fast, you can go over the max cookie size.