hannob 338 days ago [-]
The part about man in the middle attacks is as far as I can tell dangerously misleading.

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.

buildme 337 days ago [-]
Caveat: only do this if you're absolutely certain you never want to make a plain HTTP request ever again, because once you're on each browser's HSTS preload list, it takes months to get removed and have the updated list propagated out to browsers. [1]

[1] https://www.troyhunt.com/understanding-http-strict-transport...

joncalhoun 336 days ago [-]
Huh... I was thinking I touched on the Secure field of the http.Cookie when I wrote this article, but I guess I did not. I'll try to find time to update the article with both the Secure field and more info on HSTS.
weitzj 338 days ago [-]
Well rounded article. The securecookie from Gorilla works quite nicely. A good feature is that you can have key-rotation in the server-side signature function and can gradually introduce new keys to sign your cookies
gcb0 338 days ago [-]
Not requiring explicit parameters for things like domain, etc in this day and age is just ridiculous.

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".

elithrar 338 days ago [-]
On this topic, and as a co-maintainer (but not original author) of gorilla/securecookie, which is mentioned in this article:

- 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

jrockway 338 days ago [-]
I'd personally prefer that the low-level core library just handle encoding and decoding. Any rules you want to add on top of that, just add on top of that.
jorams 338 days ago [-]
> Not requiring explicit parameters for things like domain, etc in this day and age is just ridiculous.

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".

elithrar 338 days ago [-]
Correct; the same goes for Path, which is often a fun bug for many newer web programmers when they set the cookie on '/login' and then wonder why the cookie isn't being passed on '/store' or even '/'.
paulddraper 338 days ago [-]
FWIW, that's exactly how it works in client-side browsers scripting.

    document.cookie = 'name=value';
gcb0 338 days ago [-]
client side cookoes are already inherently unsafe.

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.

chmike 338 days ago [-]
The passage about Gorilla's securecookie is misleading. JWT only sign the data. It doesn't encypher it. The Gorilla's securecookie, encypher the data and signs the encyphered data, the date and the cookie name.

Note also the existence of github.com/chmike/securecookie which is as secured, and more efficient in size, number of allocations and speed.

eecc 338 days ago [-]
“SetCookie doesn't return an error”, in a core library. I guess there’s a rationalization to this folly.
jrockway 338 days ago [-]
It does very little that can cause an error:


    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.
regecks 338 days ago [-]
Whoever wrote that part of the net/http package probably wanted `Cookie` to fulfill the `Stringer` interface, so the compromise was to use an empty string.

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.

ThePadawan 338 days ago [-]
I would start arguing about the semantics of "causing an error" here, but I don't think it would as productive as reducing it down to this question.

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.

im_dario 338 days ago [-]
Setting a cookie translates to writing a header down the socket but it isn't written when SetCookie is called. It is written when you use the ResponseWriter as a parameter for a writer method (io.WriteString, for instance).

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.

jrockway 337 days ago [-]
How far do you take this, though? If you call 'w.Header().Add("Set-Cooike", "my=cookie")' you're not going to get your cookie either. Header.Add could check the key and say "hey, that's not in the HTTP standard, did you mean Set-Cookie?" but that would be annoying when you want to extend the standard. But OK, you can only extend the standard by prefixing the header with X-, so allow that. But what if you want to set X-Frame-Options and misspell it as "X-Farme-Options"? That one is kind of standard, though, so I guess that could be checked. But what if you want to set options on a Farme?

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")
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.
denormalfloat 338 days ago [-]
Encrypting or signing cookies quickly becomes painful when you have multiple front ends. Either they all need to coordinate around the signing key, or they all need to share the same key, and they need to synchronize on key rotation.
intothemild 338 days ago [-]
multiple frontends as in multiple instances? or different instances.

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.

zellyn 338 days ago [-]
It's interesting that they're comparing Go the language and Rails the framework. Obviously, it's apples-to-oranges, but it makes sense that people would think about it that way.