mirror of https://github.com/golang/go.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
eb31a0f063
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>
This commit is contained in:
parent
4da84adc0c
commit
3d5703babe
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue