base64 cookie support
This commit is contained in:
parent
57f82ed71e
commit
cdebfd6436
@ -85,8 +85,8 @@ type Cipher struct {
|
||||
}
|
||||
|
||||
// NewCipher returns a new aes Cipher for encrypting cookie values
|
||||
func NewCipher(secret string) (*Cipher, error) {
|
||||
c, err := aes.NewCipher([]byte(secret))
|
||||
func NewCipher(secret []byte) (*Cipher, error) {
|
||||
c, err := aes.NewCipher(secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cookie
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/bmizerany/assert"
|
||||
@ -9,7 +10,25 @@ import (
|
||||
func TestEncodeAndDecodeAccessToken(t *testing.T) {
|
||||
const secret = "0123456789abcdefghijklmnopqrstuv"
|
||||
const token = "my access token"
|
||||
c, err := NewCipher(secret)
|
||||
c, err := NewCipher([]byte(secret))
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
encoded, err := c.Encrypt(token)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
decoded, err := c.Decrypt(encoded)
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
assert.NotEqual(t, token, encoded)
|
||||
assert.Equal(t, token, decoded)
|
||||
}
|
||||
|
||||
func TestEncodeAndDecodeAccessTokenB64(t *testing.T) {
|
||||
const secret_b64 = "A3Xbr6fu6Al0HkgrP1ztjb-mYiwmxgNPP-XbNsz1WBk="
|
||||
const token = "my access token"
|
||||
|
||||
secret, err := base64.URLEncoding.DecodeString(secret_b64)
|
||||
c, err := NewCipher([]byte(secret))
|
||||
assert.Equal(t, nil, err)
|
||||
|
||||
encoded, err := c.Encrypt(token)
|
||||
|
2
main.go
2
main.go
@ -55,7 +55,7 @@ func main() {
|
||||
flagSet.String("proxy-prefix", "/oauth2", "the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in)")
|
||||
|
||||
flagSet.String("cookie-name", "_oauth2_proxy", "the name of the cookie that the oauth_proxy creates")
|
||||
flagSet.String("cookie-secret", "", "the seed string for secure cookies")
|
||||
flagSet.String("cookie-secret", "", "the seed string for secure cookies (optionally base64 encoded)")
|
||||
flagSet.String("cookie-domain", "", "an optional cookie domain to force cookies to (ie: .yourcompany.com)*")
|
||||
flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
|
||||
flagSet.Duration("cookie-refresh", time.Duration(0), "refresh the cookie after this duration; 0 to disable")
|
||||
|
@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
b64 "encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
@ -164,10 +164,9 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
|
||||
var cipher *cookie.Cipher
|
||||
if opts.PassAccessToken || (opts.CookieRefresh != time.Duration(0)) {
|
||||
var err error
|
||||
cipher, err = cookie.NewCipher(opts.CookieSecret)
|
||||
cipher, err = cookie.NewCipher(secretBytes(opts.CookieSecret))
|
||||
if err != nil {
|
||||
log.Fatal("error creating AES cipher with "+
|
||||
"cookie-secret ", opts.CookieSecret, ": ", err)
|
||||
log.Fatal("cookie-secret error: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -626,7 +625,7 @@ func (p *OAuthProxy) CheckBasicAuth(req *http.Request) (*providers.SessionState,
|
||||
if len(s) != 2 || s[0] != "Basic" {
|
||||
return nil, fmt.Errorf("invalid Authorization header %s", req.Header.Get("Authorization"))
|
||||
}
|
||||
b, err := base64.StdEncoding.DecodeString(s[1])
|
||||
b, err := b64.StdEncoding.DecodeString(s[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -427,7 +427,7 @@ func NewProcessCookieTest(opts ProcessCookieTestOpts) *ProcessCookieTest {
|
||||
pc_test.opts = NewOptions()
|
||||
pc_test.opts.ClientID = "bazquux"
|
||||
pc_test.opts.ClientSecret = "xyzzyplugh"
|
||||
pc_test.opts.CookieSecret = "0123456789abcdef"
|
||||
pc_test.opts.CookieSecret = "0123456789abcdefabcd"
|
||||
// First, set the CookieRefresh option so proxy.AesCipher is created,
|
||||
// needed to encrypt the access_token.
|
||||
pc_test.opts.CookieRefresh = time.Hour
|
||||
|
38
options.go
38
options.go
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
@ -156,17 +157,25 @@ func (o *Options) Validate() error {
|
||||
if o.PassAccessToken || (o.CookieRefresh != time.Duration(0)) {
|
||||
valid_cookie_secret_size := false
|
||||
for _, i := range []int{16, 24, 32} {
|
||||
if len(o.CookieSecret) == i {
|
||||
if len(secretBytes(o.CookieSecret)) == i {
|
||||
valid_cookie_secret_size = true
|
||||
}
|
||||
}
|
||||
var decoded bool
|
||||
if string(secretBytes(o.CookieSecret)) != o.CookieSecret {
|
||||
decoded = true
|
||||
}
|
||||
if valid_cookie_secret_size == false {
|
||||
var suffix string
|
||||
if decoded {
|
||||
suffix = fmt.Sprintf(" note: cookie secret was base64 decoded from %q", o.CookieSecret)
|
||||
}
|
||||
msgs = append(msgs, fmt.Sprintf(
|
||||
"cookie_secret must be 16, 24, or 32 bytes "+
|
||||
"to create an AES cipher when "+
|
||||
"pass_access_token == true or "+
|
||||
"cookie_refresh != 0, but is %d bytes",
|
||||
len(o.CookieSecret)))
|
||||
"cookie_refresh != 0, but is %d bytes.%s",
|
||||
len(secretBytes(o.CookieSecret)), suffix))
|
||||
}
|
||||
}
|
||||
|
||||
@ -251,3 +260,26 @@ func parseSignatureKey(o *Options, msgs []string) []string {
|
||||
}
|
||||
return msgs
|
||||
}
|
||||
|
||||
func addPadding(secret string) string {
|
||||
padding := len(secret) % 4
|
||||
switch padding {
|
||||
case 1:
|
||||
return secret + "==="
|
||||
case 2:
|
||||
return secret + "=="
|
||||
case 3:
|
||||
return secret + "="
|
||||
default:
|
||||
return secret
|
||||
}
|
||||
}
|
||||
|
||||
// secretBytes attempts to base64 decode the secret, if that fails it treats the secret as binary
|
||||
func secretBytes(secret string) []byte {
|
||||
b, err := base64.URLEncoding.DecodeString(addPadding(secret))
|
||||
if err == nil {
|
||||
return []byte(addPadding(string(b)))
|
||||
}
|
||||
return []byte(secret)
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ func TestCookieRefreshMustBeLessThanCookieExpire(t *testing.T) {
|
||||
o := testOptions()
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
o.CookieSecret = "0123456789abcdef"
|
||||
o.CookieSecret = "0123456789abcdefabcd"
|
||||
o.CookieRefresh = o.CookieExpire
|
||||
assert.NotEqual(t, nil, o.Validate())
|
||||
|
||||
@ -168,6 +168,31 @@ func TestCookieRefreshMustBeLessThanCookieExpire(t *testing.T) {
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
}
|
||||
|
||||
func TestBase64CookieSecret(t *testing.T) {
|
||||
o := testOptions()
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 32 byte, base64 (urlsafe) encoded key
|
||||
o.CookieSecret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ="
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 32 byte, base64 (urlsafe) encoded key, w/o padding
|
||||
o.CookieSecret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 24 byte, base64 (urlsafe) encoded key
|
||||
o.CookieSecret = "Kp33Gj-GQmYtz4zZUyUDdqQKx5_Hgkv3"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 16 byte, base64 (urlsafe) encoded key
|
||||
o.CookieSecret = "LFEqZYvYUwKwzn0tEuTpLA=="
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
|
||||
// 16 byte, base64 (urlsafe) encoded key, w/o padding
|
||||
o.CookieSecret = "LFEqZYvYUwKwzn0tEuTpLA"
|
||||
assert.Equal(t, nil, o.Validate())
|
||||
}
|
||||
|
||||
func TestValidateSignatureKey(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.SignatureKey = "sha1:secret"
|
||||
|
@ -3,11 +3,11 @@ package providers
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type GitHubProvider struct {
|
||||
@ -64,7 +64,7 @@ func (p *GitHubProvider) hasOrg(accessToken string) (bool, error) {
|
||||
"limit": {"100"},
|
||||
}
|
||||
|
||||
endpoint := p.ValidateURL.Scheme + "://" + p.ValidateURL.Host + "/user/orgs?" + params.Encode()
|
||||
endpoint := p.ValidateURL.Scheme + "://" + p.ValidateURL.Host + "/user/orgs?" + params.Encode()
|
||||
req, _ := http.NewRequest("GET", endpoint, nil)
|
||||
req.Header.Set("Accept", "application/vnd.github.v3+json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
|
@ -13,9 +13,9 @@ const secret = "0123456789abcdefghijklmnopqrstuv"
|
||||
const altSecret = "0000000000abcdefghijklmnopqrstuv"
|
||||
|
||||
func TestSessionStateSerialization(t *testing.T) {
|
||||
c, err := cookie.NewCipher(secret)
|
||||
c, err := cookie.NewCipher([]byte(secret))
|
||||
assert.Equal(t, nil, err)
|
||||
c2, err := cookie.NewCipher(altSecret)
|
||||
c2, err := cookie.NewCipher([]byte(altSecret))
|
||||
assert.Equal(t, nil, err)
|
||||
s := &SessionState{
|
||||
Email: "user@domain.com",
|
||||
|
Loading…
Reference in New Issue
Block a user