]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: add support for SameSite option in http.Cookie
authorStephan Renatus <srenatus@chef.io>
Mon, 27 Nov 2017 11:41:10 +0000 (12:41 +0100)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 9 Jul 2018 19:58:29 +0000 (19:58 +0000)
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 <reed@hackerone.com>

Change-Id: I98c8a9a92358b2f632990576879759e3aff38cff
Reviewed-on: https://go-review.googlesource.com/79919
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/net/http/cookie.go
src/net/http/cookie_test.go
src/net/http/cookiejar/jar.go

index 09b89748623677b3d5d39a04cb92377b76f3ea33..b1a6cef6f700f11002b18c31172647f63a601bb7 100644 (file)
@@ -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()
 }
 
index 9d199a3752e0f04ad641b4cc9ea3c6c0d11f0fc8..022adaa90def4fa608b62b2d48c0da1906cadb2c 100644 (file)
@@ -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:
        {
index ef8c35bf0a167700e5fc41f2e66df2978923783f..9f1991708472b9a0a2438c2a0334c23be352b20b 100644 (file)
@@ -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
 }