git.iko.soy/typeable/oauth2-proxy
on master help sign in
Migrate cookie signing to SHA256 from SHA1 (#524)

Also, cleanup the code & make the specific hashing algorithm chosen a function variable. Co-authored-by: Henry Jenkins <henry@henryjenkins.name>

commit9d626265e8c2cef0917702f653047f486040a018 authorNick Meves <nick.meves@greenhouse.io> dateTue 5 May 2020, 18:41 (6 years ago) parent07df29d
3 files changed+37−5
CHANGELOG.md+1 pkg/encryption/cipher.go+17−5 pkg/encryption/cipher_test.go+19
CHANGELOG.md+1
@@ -25,6 +25,7 @@
2525
2626## Changes since v5.1.1
2727
28+- [#524](https://github.com/oauth2-proxy/oauth2-proxy/pull/524) Sign cookies with SHA256 (@NickMeves)
2829- [#515](https://github.com/oauth2-proxy/oauth2-proxy/pull/515) Drop configure script in favour of native Makefile env and checks (@JoelSpeed)
2930- [#487](https://github.com/oauth2-proxy/oauth2-proxy/pull/487) Switch flags to PFlag to remove StringArray (@JoelSpeed)
3031- [#484](https://github.com/oauth2-proxy/oauth2-proxy/pull/484) Replace configuration loading with Viper (@JoelSpeed)
pkg/encryption/cipher.go+17−5
@@ -6,8 +6,10 @@ import (
66 "crypto/hmac"
77 "crypto/rand"
88 "crypto/sha1"
9+ "crypto/sha256"
910 "encoding/base64"
1011 "fmt"
12+ "hash"
1113 "io"
1214 "net/http"
1315 "strconv"
@@ -25,8 +27,7 @@ func Validate(cookie *http.Cookie, seed string, expiration time.Duration) (value
2527 if len(parts) != 3 {
2628 return
2729 }
28− sig := cookieSignature(seed, cookie.Name, parts[0], parts[1])
29− if checkHmac(parts[2], sig) {
30+ if checkSignature(parts[2], seed, cookie.Name, parts[0], parts[1]) {
3031 ts, err := strconv.Atoi(parts[1])
3132 if err != nil {
3233 return
@@ -53,13 +54,13 @@ func Validate(cookie *http.Cookie, seed string, expiration time.Duration) (value
5354func SignedValue(seed string, key string, value string, now time.Time) string {
5455 encodedValue := base64.URLEncoding.EncodeToString([]byte(value))
5556 timeStr := fmt.Sprintf("%d", now.Unix())
56− sig := cookieSignature(seed, key, encodedValue, timeStr)
57+ sig := cookieSignature(sha256.New, seed, key, encodedValue, timeStr)
5758 cookieVal := fmt.Sprintf("%s|%s|%s", encodedValue, timeStr, sig)
5859 return cookieVal
5960}
6061
61−func cookieSignature(args ...string) string {
62− h := hmac.New(sha1.New, []byte(args[0]))
62+func cookieSignature(signer func() hash.Hash, args ...string) string {
63+ h := hmac.New(signer, []byte(args[0]))
6364 for _, arg := range args[1:] {
6465 h.Write([]byte(arg))
6566 }
@@ -68,6 +69,17 @@ func cookieSignature(args ...string) string {
6869 return base64.URLEncoding.EncodeToString(b)
6970}
7071
72+func checkSignature(signature string, args ...string) bool {
73+ checkSig := cookieSignature(sha256.New, args...)
74+ if checkHmac(signature, checkSig) {
75+ return true
76+ }
77+
78+ // TODO: After appropriate rollout window, remove support for SHA1
79+ legacySig := cookieSignature(sha1.New, args...)
80+ return checkHmac(signature, legacySig)
81+}
82+
7183func checkHmac(input, expected string) bool {
7284 inputMAC, err1 := base64.URLEncoding.DecodeString(input)
7385 if err1 == nil {
pkg/encryption/cipher_test.go+19
@@ -1,12 +1,31 @@
11package encryption
22
33import (
4+ "crypto/sha1"
5+ "crypto/sha256"
46 "encoding/base64"
57 "testing"
68
79 "github.com/stretchr/testify/assert"
810)
911
12+func TestSignAndValidate(t *testing.T) {
13+ seed := "0123456789abcdef"
14+ key := "cookie-name"
15+ value := base64.URLEncoding.EncodeToString([]byte("I am soooo encoded"))
16+ epoch := "123456789"
17+
18+ sha256sig := cookieSignature(sha256.New, seed, key, value, epoch)
19+ sha1sig := cookieSignature(sha1.New, seed, key, value, epoch)
20+
21+ assert.True(t, checkSignature(sha256sig, seed, key, value, epoch))
22+ // This should be switched to False after fully deprecating SHA1
23+ assert.True(t, checkSignature(sha1sig, seed, key, value, epoch))
24+
25+ assert.False(t, checkSignature(sha256sig, seed, key, "tampered", epoch))
26+ assert.False(t, checkSignature(sha1sig, seed, key, "tampered", epoch))
27+}
28+
1029func TestEncodeAndDecodeAccessToken(t *testing.T) {
1130 const secret = "0123456789abcdefghijklmnopqrstuv"
1231 const token = "my access token"