From: Stephan Renatus Date: Mon, 27 Nov 2017 11:41:10 +0000 (+0100) Subject: net/http: add support for SameSite option in http.Cookie X-Git-Tag: go1.11beta2~181 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=3d5703babe9c5344252db3fb8e96f20cd036535a;p=gostls13.git net/http: add support for SameSite option in http.Cookie The same-site cookie attribute prevents a cookie from being sent along with cross-site requests. The main goal is mitigate the risk of cross-origin information leakage and provides some protection against cross-site request forgery attacks. This change adds the option to http.Cookie so it can be stored and passed to HTTP clients. Spec: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 Fixes #15867 Based on https://github.com/reedloden/go/commit/eb31a0f063c80058bbb3abff4ca09b3565985500 by Reed Loden Change-Id: I98c8a9a92358b2f632990576879759e3aff38cff Reviewed-on: https://go-review.googlesource.com/79919 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go index 09b8974862..b1a6cef6f7 100644 --- a/src/net/http/cookie.go +++ b/src/net/http/cookie.go @@ -31,10 +31,25 @@ type Cookie struct { MaxAge int Secure bool HttpOnly bool + SameSite SameSite Raw string Unparsed []string // Raw text of unparsed attribute-value pairs } +// SameSite allows a server define a cookie attribute making it impossible to +// the browser send this cookie along with cross-site requests. The main goal +// is mitigate the risk of cross-origin information leakage, and provides some +// protection against cross-site request forgery attacks. +// +// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details. +type SameSite int + +const ( + SameSiteDefaultMode SameSite = iota + 1 + SameSiteLaxMode + SameSiteStrictMode +) + // readSetCookies parses all "Set-Cookie" values from // the header h and returns the successfully parsed Cookies. func readSetCookies(h Header) []*Cookie { @@ -83,6 +98,17 @@ func readSetCookies(h Header) []*Cookie { continue } switch lowerAttr { + case "samesite": + lowerVal := strings.ToLower(val) + switch lowerVal { + case "lax": + c.SameSite = SameSiteLaxMode + case "strict": + c.SameSite = SameSiteStrictMode + default: + c.SameSite = SameSiteDefaultMode + } + continue case "secure": c.Secure = true continue @@ -184,6 +210,14 @@ func (c *Cookie) String() string { if c.Secure { b.WriteString("; Secure") } + switch c.SameSite { + case SameSiteDefaultMode: + b.WriteString("; SameSite") + case SameSiteLaxMode: + b.WriteString("; SameSite=Lax") + case SameSiteStrictMode: + b.WriteString("; SameSite=Strict") + } return b.String() } diff --git a/src/net/http/cookie_test.go b/src/net/http/cookie_test.go index 9d199a3752..022adaa90d 100644 --- a/src/net/http/cookie_test.go +++ b/src/net/http/cookie_test.go @@ -65,6 +65,18 @@ var writeSetCookiesTests = []struct { &Cookie{Name: "cookie-11", Value: "invalid-expiry", Expires: time.Date(1600, 1, 1, 1, 1, 1, 1, time.UTC)}, "cookie-11=invalid-expiry", }, + { + &Cookie{Name: "cookie-12", Value: "samesite-default", SameSite: SameSiteDefaultMode}, + "cookie-12=samesite-default; SameSite", + }, + { + &Cookie{Name: "cookie-13", Value: "samesite-lax", SameSite: SameSiteLaxMode}, + "cookie-13=samesite-lax; SameSite=Lax", + }, + { + &Cookie{Name: "cookie-14", Value: "samesite-strict", SameSite: SameSiteStrictMode}, + "cookie-14=samesite-strict; SameSite=Strict", + }, // The "special" cookies have values containing commas or spaces which // are disallowed by RFC 6265 but are common in the wild. { @@ -241,6 +253,33 @@ var readSetCookiesTests = []struct { Raw: "ASP.NET_SessionId=foo; path=/; HttpOnly", }}, }, + { + Header{"Set-Cookie": {"samesitedefault=foo; SameSite"}}, + []*Cookie{{ + Name: "samesitedefault", + Value: "foo", + SameSite: SameSiteDefaultMode, + Raw: "samesitedefault=foo; SameSite", + }}, + }, + { + Header{"Set-Cookie": {"samesitelax=foo; SameSite=Lax"}}, + []*Cookie{{ + Name: "samesitelax", + Value: "foo", + SameSite: SameSiteLaxMode, + Raw: "samesitelax=foo; SameSite=Lax", + }}, + }, + { + Header{"Set-Cookie": {"samesitestrict=foo; SameSite=Strict"}}, + []*Cookie{{ + Name: "samesitestrict", + Value: "foo", + SameSite: SameSiteStrictMode, + Raw: "samesitestrict=foo; SameSite=Strict", + }}, + }, // Make sure we can properly read back the Set-Cookie headers we create // for values containing spaces or commas: { diff --git a/src/net/http/cookiejar/jar.go b/src/net/http/cookiejar/jar.go index ef8c35bf0a..9f19917084 100644 --- a/src/net/http/cookiejar/jar.go +++ b/src/net/http/cookiejar/jar.go @@ -93,6 +93,7 @@ type entry struct { Value string Domain string Path string + SameSite string Secure bool HttpOnly bool Persistent bool @@ -418,6 +419,15 @@ func (j *Jar) newEntry(c *http.Cookie, now time.Time, defPath, host string) (e e e.Secure = c.Secure e.HttpOnly = c.HttpOnly + switch c.SameSite { + case http.SameSiteDefaultMode: + e.SameSite = "SameSite" + case http.SameSiteStrictMode: + e.SameSite = "SameSite=Strict" + case http.SameSiteLaxMode: + e.SameSite = "SameSite=Lax" + } + return e, false, nil }