Split large cookies
This commit is contained in:
parent
7b7cc8fdc4
commit
bcd5ac513c
104
oauthproxy.go
104
oauthproxy.go
@ -260,15 +260,92 @@ func (p *OAuthProxy) redeemCode(host, code string) (s *providers.SessionState, e
|
||||
return
|
||||
}
|
||||
|
||||
func (p *OAuthProxy) MakeSessionCookie(req *http.Request, value string, expiration time.Duration, now time.Time) *http.Cookie {
|
||||
func (p *OAuthProxy) MakeSessionCookie(req *http.Request, value string, expiration time.Duration, now time.Time) []*http.Cookie {
|
||||
if value != "" {
|
||||
value = cookie.SignedValue(p.CookieSeed, p.CookieName, value, now)
|
||||
if len(value) > 4096 {
|
||||
// Cookies cannot be larger than 4kb
|
||||
log.Printf("WARNING - Cookie Size: %d bytes", len(value))
|
||||
}
|
||||
c := p.makeCookie(req, p.CookieName, value, expiration, now)
|
||||
if len(c.Value) > 4096 {
|
||||
return splitCookie(c)
|
||||
}
|
||||
return []*http.Cookie{c}
|
||||
}
|
||||
|
||||
func copyCookie(c *http.Cookie) *http.Cookie {
|
||||
return &http.Cookie{
|
||||
Name: c.Name,
|
||||
Value: c.Value,
|
||||
Path: c.Path,
|
||||
Domain: c.Domain,
|
||||
Expires: c.Expires,
|
||||
RawExpires: c.RawExpires,
|
||||
MaxAge: c.MaxAge,
|
||||
Secure: c.Secure,
|
||||
HttpOnly: c.HttpOnly,
|
||||
Raw: c.Raw,
|
||||
Unparsed: c.Unparsed,
|
||||
}
|
||||
}
|
||||
|
||||
func splitCookie(c *http.Cookie) []*http.Cookie {
|
||||
if len(c.Value) < 3840 {
|
||||
return []*http.Cookie{c}
|
||||
}
|
||||
cookies := []*http.Cookie{}
|
||||
valueBytes := []byte(c.Value)
|
||||
count := 0
|
||||
for len(valueBytes) > 0 {
|
||||
new := copyCookie(c)
|
||||
new.Name = fmt.Sprintf("%s-%d", c.Name, count)
|
||||
count++
|
||||
if len(valueBytes) < 3840 {
|
||||
new.Value = string(valueBytes)
|
||||
valueBytes = []byte{}
|
||||
} else {
|
||||
newValue := valueBytes[:3840]
|
||||
valueBytes = valueBytes[3840:]
|
||||
new.Value = string(newValue)
|
||||
}
|
||||
cookies = append(cookies, new)
|
||||
}
|
||||
return cookies
|
||||
}
|
||||
|
||||
func joinCookies(cookies []*http.Cookie) (*http.Cookie, error) {
|
||||
if len(cookies) == 0 {
|
||||
return nil, fmt.Errorf("Could not load cookie.")
|
||||
}
|
||||
if len(cookies) == 1 {
|
||||
return cookies[0], nil
|
||||
}
|
||||
c := copyCookie(cookies[0])
|
||||
for i := 1; i < len(cookies); i++ {
|
||||
c.Value += cookies[i].Value
|
||||
}
|
||||
c.Name = strings.TrimRight(c.Name, "-0")
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func loadCookie(req *http.Request, cookieName string) (*http.Cookie, error) {
|
||||
c, err := req.Cookie(cookieName)
|
||||
if err == nil {
|
||||
return c, nil
|
||||
}
|
||||
cookies := []*http.Cookie{}
|
||||
err = nil
|
||||
count := 0
|
||||
for err == nil {
|
||||
var c *http.Cookie
|
||||
c, err = req.Cookie(fmt.Sprintf("%s-%d", cookieName, count))
|
||||
if err == nil {
|
||||
cookies = append(cookies, c)
|
||||
count++
|
||||
}
|
||||
}
|
||||
return p.makeCookie(req, p.CookieName, value, expiration, now)
|
||||
if len(cookies) == 0 {
|
||||
return nil, fmt.Errorf("Could not find cookie %s", cookieName)
|
||||
}
|
||||
return joinCookies(cookies)
|
||||
}
|
||||
|
||||
func (p *OAuthProxy) MakeCSRFCookie(req *http.Request, value string, expiration time.Duration, now time.Time) *http.Cookie {
|
||||
@ -298,6 +375,7 @@ func (p *OAuthProxy) makeCookie(req *http.Request, name string, value string, ex
|
||||
}
|
||||
|
||||
func (p *OAuthProxy) ClearCSRFCookie(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
http.SetCookie(rw, p.MakeCSRFCookie(req, "", time.Hour*-1, time.Now()))
|
||||
}
|
||||
|
||||
@ -306,24 +384,28 @@ func (p *OAuthProxy) SetCSRFCookie(rw http.ResponseWriter, req *http.Request, va
|
||||
}
|
||||
|
||||
func (p *OAuthProxy) ClearSessionCookie(rw http.ResponseWriter, req *http.Request) {
|
||||
clr := p.MakeSessionCookie(req, "", time.Hour*-1, time.Now())
|
||||
http.SetCookie(rw, clr)
|
||||
cookies := p.MakeSessionCookie(req, "", time.Hour*-1, time.Now())
|
||||
for _, clr := range cookies {
|
||||
http.SetCookie(rw, clr)
|
||||
}
|
||||
|
||||
// ugly hack because default domain changed
|
||||
if p.CookieDomain == "" {
|
||||
clr2 := *clr
|
||||
if p.CookieDomain == "" && len(cookies) > 0 {
|
||||
clr2 := *cookies[0]
|
||||
clr2.Domain = req.Host
|
||||
http.SetCookie(rw, &clr2)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *OAuthProxy) SetSessionCookie(rw http.ResponseWriter, req *http.Request, val string) {
|
||||
http.SetCookie(rw, p.MakeSessionCookie(req, val, p.CookieExpire, time.Now()))
|
||||
for _, c := range p.MakeSessionCookie(req, val, p.CookieExpire, time.Now()) {
|
||||
http.SetCookie(rw, c)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *OAuthProxy) LoadCookiedSession(req *http.Request) (*providers.SessionState, time.Duration, error) {
|
||||
var age time.Duration
|
||||
c, err := req.Cookie(p.CookieName)
|
||||
c, err := loadCookie(req, p.CookieName)
|
||||
if err != nil {
|
||||
// always http.ErrNoCookie
|
||||
return nil, age, fmt.Errorf("Cookie %q not present", p.CookieName)
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -143,6 +144,73 @@ func TestIsValidRedirect(t *testing.T) {
|
||||
assert.Equal(t, false, invalidHttps2)
|
||||
}
|
||||
|
||||
func randomString(length int) string {
|
||||
charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[seededRand.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func TestSplitCookie(t *testing.T) {
|
||||
c1 := &http.Cookie{
|
||||
Name: "cookie-name",
|
||||
Value: randomString(5120),
|
||||
Path: "/",
|
||||
Domain: "foo.bar",
|
||||
HttpOnly: true,
|
||||
Secure: true,
|
||||
Expires: time.Now(),
|
||||
}
|
||||
cookies := splitCookie(c1)
|
||||
assert.Equal(t, 2, len(cookies))
|
||||
|
||||
assert.Equal(t, c1.Name+"-0", cookies[0].Name)
|
||||
assert.Equal(t, c1.Name+"-1", cookies[1].Name)
|
||||
|
||||
assert.Equal(t, 3840, len(cookies[0].Value))
|
||||
assert.Equal(t, 5120-3840, len(cookies[1].Value))
|
||||
|
||||
c2 := &http.Cookie{
|
||||
Name: "cookie-name",
|
||||
Value: randomString(3000),
|
||||
Path: "/",
|
||||
Domain: "foo.bar",
|
||||
HttpOnly: true,
|
||||
Secure: true,
|
||||
Expires: time.Now(),
|
||||
}
|
||||
|
||||
cookies2 := splitCookie(c2)
|
||||
assert.Equal(t, 1, len(cookies2))
|
||||
|
||||
assert.Equal(t, c2.Name, cookies2[0].Name)
|
||||
assert.Equal(t, c2.Value, cookies2[0].Value)
|
||||
}
|
||||
|
||||
func TestJoinCookies(t *testing.T) {
|
||||
c1 := &http.Cookie{
|
||||
Name: "cookie-name",
|
||||
Value: randomString(5120),
|
||||
Path: "/",
|
||||
Domain: "foo.bar",
|
||||
HttpOnly: true,
|
||||
Secure: true,
|
||||
Expires: time.Now(),
|
||||
}
|
||||
// Split Cookies
|
||||
cookies := splitCookie(c1)
|
||||
assert.Equal(t, 2, len(cookies))
|
||||
|
||||
// join cookies should be the ivnerse
|
||||
c2, _ := joinCookies(cookies)
|
||||
|
||||
assert.Equal(t, c1.Name, c2.Name)
|
||||
assert.Equal(t, c1.Value, c2.Value)
|
||||
}
|
||||
|
||||
type TestProvider struct {
|
||||
*providers.ProviderData
|
||||
EmailAddress string
|
||||
@ -555,7 +623,7 @@ func NewProcessCookieTestWithDefaults() *ProcessCookieTest {
|
||||
})
|
||||
}
|
||||
|
||||
func (p *ProcessCookieTest) MakeCookie(value string, ref time.Time) *http.Cookie {
|
||||
func (p *ProcessCookieTest) MakeCookie(value string, ref time.Time) []*http.Cookie {
|
||||
return p.proxy.MakeSessionCookie(p.req, value, p.opts.CookieExpire, ref)
|
||||
}
|
||||
|
||||
@ -564,7 +632,9 @@ func (p *ProcessCookieTest) SaveSession(s *providers.SessionState, ref time.Time
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.req.AddCookie(p.proxy.MakeSessionCookie(p.req, value, p.proxy.CookieExpire, ref))
|
||||
for _, c := range p.proxy.MakeSessionCookie(p.req, value, p.proxy.CookieExpire, ref) {
|
||||
p.req.AddCookie(c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -853,8 +923,9 @@ func (st *SignatureTest) MakeRequestWithExpectedKey(method, body, key string) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cookie := proxy.MakeSessionCookie(req, value, proxy.CookieExpire, time.Now())
|
||||
req.AddCookie(cookie)
|
||||
for _, c := range proxy.MakeSessionCookie(req, value, proxy.CookieExpire, time.Now()) {
|
||||
req.AddCookie(c)
|
||||
}
|
||||
// This is used by the upstream to validate the signature.
|
||||
st.authenticator.auth = hmacauth.NewHmacAuth(
|
||||
crypto.SHA1, []byte(key), SignatureHeader, SignatureHeaders)
|
||||
|
Loading…
Reference in New Issue
Block a user