mirror of
https://github.com/golang/go
synced 2024-11-25 17:17:56 -07:00
net/http: do not send malformed cookie domain attribute
Malformed domain attributes are not sent in a Set-Cookie header. Instead the domain attribute is dropped which turns the cookie into a host-only cookie. This is much safer than dropping characters from domain attribute. Domain attributes with a leading dot '.' are still allowed, even if discouraged by RFC 6265 section 4.1.1. Fixes #6013 R=golang-dev, bradfitz CC=golang-dev https://golang.org/cl/12745043
This commit is contained in:
parent
e838334beb
commit
4f86a96ac9
@ -8,6 +8,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -145,7 +146,15 @@ func (c *Cookie) String() string {
|
|||||||
fmt.Fprintf(&b, "; Path=%s", sanitizeCookiePath(c.Path))
|
fmt.Fprintf(&b, "; Path=%s", sanitizeCookiePath(c.Path))
|
||||||
}
|
}
|
||||||
if len(c.Domain) > 0 {
|
if len(c.Domain) > 0 {
|
||||||
fmt.Fprintf(&b, "; Domain=%s", sanitizeCookieDomain(c.Domain))
|
if validCookieDomain(c.Domain) {
|
||||||
|
// A c.Domain containing illegal characters is not
|
||||||
|
// sanitized but simply dropped which turns the cookie
|
||||||
|
// into a host-only cookie.
|
||||||
|
fmt.Fprintf(&b, "; Domain=%s", c.Domain)
|
||||||
|
} else {
|
||||||
|
log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute",
|
||||||
|
c.Domain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if c.Expires.Unix() > 0 {
|
if c.Expires.Unix() > 0 {
|
||||||
fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(time.RFC1123))
|
fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(time.RFC1123))
|
||||||
@ -208,26 +217,78 @@ func readCookies(h Header, filter string) []*Cookie {
|
|||||||
return cookies
|
return cookies
|
||||||
}
|
}
|
||||||
|
|
||||||
var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")
|
// validCookieDomain returns wheter v is a valid cookie domain-value.
|
||||||
|
func validCookieDomain(v string) bool {
|
||||||
// http://tools.ietf.org/html/rfc6265#section-4.1.1
|
if isCookieDomainName(v) {
|
||||||
// domain-av = "Domain=" domain-value
|
return true
|
||||||
// domain-value = <subdomain>
|
}
|
||||||
// ; defined in [RFC1034], Section 3.5, as
|
if net.ParseIP(v) != nil && !strings.Contains(v, ":") {
|
||||||
// ; enhanced by [RFC1123], Section 2.1
|
return true
|
||||||
func sanitizeCookieDomain(v string) string {
|
}
|
||||||
// TODO: implement http://tools.ietf.org/html/rfc1034#section-3.5
|
return false
|
||||||
return oldCookieValueSanitizer.Replace(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isCookieDomainName returns whether s is a valid domain name or a valid
|
||||||
|
// domain name with a leading dot '.'. It is almost a direct copy of
|
||||||
|
// package net's isDomainName.
|
||||||
|
func isCookieDomainName(s string) bool {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(s) > 255 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if s[0] == '.' {
|
||||||
|
// A cookie a domain attribute may start with a leading dot.
|
||||||
|
s = s[1:]
|
||||||
|
}
|
||||||
|
last := byte('.')
|
||||||
|
ok := false // Ok once we've seen a letter.
|
||||||
|
partlen := 0
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
c := s[i]
|
||||||
|
switch {
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
|
||||||
|
// No '_' allowed here (in contrast to package net).
|
||||||
|
ok = true
|
||||||
|
partlen++
|
||||||
|
case '0' <= c && c <= '9':
|
||||||
|
// fine
|
||||||
|
partlen++
|
||||||
|
case c == '-':
|
||||||
|
// Byte before dash cannot be dot.
|
||||||
|
if last == '.' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
partlen++
|
||||||
|
case c == '.':
|
||||||
|
// Byte before dot cannot be dot, dash.
|
||||||
|
if last == '.' || last == '-' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if partlen > 63 || partlen == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
partlen = 0
|
||||||
|
}
|
||||||
|
last = c
|
||||||
|
}
|
||||||
|
if last == '-' || partlen > 63 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")
|
||||||
|
|
||||||
func sanitizeCookieName(n string) string {
|
func sanitizeCookieName(n string) string {
|
||||||
return cookieNameSanitizer.Replace(n)
|
return cookieNameSanitizer.Replace(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the replacer used in the original Go cookie code.
|
|
||||||
// It's not correct, but it's here for now until it's replaced.
|
|
||||||
var oldCookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ")
|
|
||||||
|
|
||||||
// http://tools.ietf.org/html/rfc6265#section-4.1.1
|
// http://tools.ietf.org/html/rfc6265#section-4.1.1
|
||||||
// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
|
// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
|
||||||
// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
|
// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
|
||||||
|
@ -32,6 +32,22 @@ var writeSetCookiesTests = []struct {
|
|||||||
&Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
|
&Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
|
||||||
"cookie-4=four; Path=/restricted/",
|
"cookie-4=four; Path=/restricted/",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
&Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"},
|
||||||
|
"cookie-5=five",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"},
|
||||||
|
"cookie-6=six",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"},
|
||||||
|
"cookie-7=seven; Domain=127.0.0.1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
|
||||||
|
"cookie-8=eight",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWriteSetCookies(t *testing.T) {
|
func TestWriteSetCookies(t *testing.T) {
|
||||||
|
Loading…
Reference in New Issue
Block a user