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>
This commit is contained in:
		
							parent
							
								
									07df29db37
								
							
						
					
					
						commit
						9d626265e8
					
				|  | @ -25,6 +25,7 @@ | ||||||
| 
 | 
 | ||||||
| ## Changes since v5.1.1 | ## Changes since v5.1.1 | ||||||
| 
 | 
 | ||||||
|  | - [#524](https://github.com/oauth2-proxy/oauth2-proxy/pull/524) Sign cookies with SHA256 (@NickMeves) | ||||||
| - [#515](https://github.com/oauth2-proxy/oauth2-proxy/pull/515) Drop configure script in favour of native Makefile env and checks (@JoelSpeed) | - [#515](https://github.com/oauth2-proxy/oauth2-proxy/pull/515) Drop configure script in favour of native Makefile env and checks (@JoelSpeed) | ||||||
| - [#487](https://github.com/oauth2-proxy/oauth2-proxy/pull/487) Switch flags to PFlag to remove StringArray (@JoelSpeed) | - [#487](https://github.com/oauth2-proxy/oauth2-proxy/pull/487) Switch flags to PFlag to remove StringArray (@JoelSpeed) | ||||||
| - [#484](https://github.com/oauth2-proxy/oauth2-proxy/pull/484) Replace configuration loading with Viper (@JoelSpeed) | - [#484](https://github.com/oauth2-proxy/oauth2-proxy/pull/484) Replace configuration loading with Viper (@JoelSpeed) | ||||||
|  |  | ||||||
|  | @ -6,8 +6,10 @@ import ( | ||||||
| 	"crypto/hmac" | 	"crypto/hmac" | ||||||
| 	"crypto/rand" | 	"crypto/rand" | ||||||
| 	"crypto/sha1" | 	"crypto/sha1" | ||||||
|  | 	"crypto/sha256" | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"hash" | ||||||
| 	"io" | 	"io" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"strconv" | 	"strconv" | ||||||
|  | @ -25,8 +27,7 @@ func Validate(cookie *http.Cookie, seed string, expiration time.Duration) (value | ||||||
| 	if len(parts) != 3 { | 	if len(parts) != 3 { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	sig := cookieSignature(seed, cookie.Name, parts[0], parts[1]) | 	if checkSignature(parts[2], seed, cookie.Name, parts[0], parts[1]) { | ||||||
| 	if checkHmac(parts[2], sig) { |  | ||||||
| 		ts, err := strconv.Atoi(parts[1]) | 		ts, err := strconv.Atoi(parts[1]) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return | 			return | ||||||
|  | @ -53,13 +54,13 @@ func Validate(cookie *http.Cookie, seed string, expiration time.Duration) (value | ||||||
| func SignedValue(seed string, key string, value string, now time.Time) string { | func SignedValue(seed string, key string, value string, now time.Time) string { | ||||||
| 	encodedValue := base64.URLEncoding.EncodeToString([]byte(value)) | 	encodedValue := base64.URLEncoding.EncodeToString([]byte(value)) | ||||||
| 	timeStr := fmt.Sprintf("%d", now.Unix()) | 	timeStr := fmt.Sprintf("%d", now.Unix()) | ||||||
| 	sig := cookieSignature(seed, key, encodedValue, timeStr) | 	sig := cookieSignature(sha256.New, seed, key, encodedValue, timeStr) | ||||||
| 	cookieVal := fmt.Sprintf("%s|%s|%s", encodedValue, timeStr, sig) | 	cookieVal := fmt.Sprintf("%s|%s|%s", encodedValue, timeStr, sig) | ||||||
| 	return cookieVal | 	return cookieVal | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func cookieSignature(args ...string) string { | func cookieSignature(signer func() hash.Hash, args ...string) string { | ||||||
| 	h := hmac.New(sha1.New, []byte(args[0])) | 	h := hmac.New(signer, []byte(args[0])) | ||||||
| 	for _, arg := range args[1:] { | 	for _, arg := range args[1:] { | ||||||
| 		h.Write([]byte(arg)) | 		h.Write([]byte(arg)) | ||||||
| 	} | 	} | ||||||
|  | @ -68,6 +69,17 @@ func cookieSignature(args ...string) string { | ||||||
| 	return base64.URLEncoding.EncodeToString(b) | 	return base64.URLEncoding.EncodeToString(b) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func checkSignature(signature string, args ...string) bool { | ||||||
|  | 	checkSig := cookieSignature(sha256.New, args...) | ||||||
|  | 	if checkHmac(signature, checkSig) { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// TODO: After appropriate rollout window, remove support for SHA1
 | ||||||
|  | 	legacySig := cookieSignature(sha1.New, args...) | ||||||
|  | 	return checkHmac(signature, legacySig) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func checkHmac(input, expected string) bool { | func checkHmac(input, expected string) bool { | ||||||
| 	inputMAC, err1 := base64.URLEncoding.DecodeString(input) | 	inputMAC, err1 := base64.URLEncoding.DecodeString(input) | ||||||
| 	if err1 == nil { | 	if err1 == nil { | ||||||
|  |  | ||||||
|  | @ -1,12 +1,31 @@ | ||||||
| package encryption | package encryption | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"crypto/sha1" | ||||||
|  | 	"crypto/sha256" | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | func TestSignAndValidate(t *testing.T) { | ||||||
|  | 	seed := "0123456789abcdef" | ||||||
|  | 	key := "cookie-name" | ||||||
|  | 	value := base64.URLEncoding.EncodeToString([]byte("I am soooo encoded")) | ||||||
|  | 	epoch := "123456789" | ||||||
|  | 
 | ||||||
|  | 	sha256sig := cookieSignature(sha256.New, seed, key, value, epoch) | ||||||
|  | 	sha1sig := cookieSignature(sha1.New, seed, key, value, epoch) | ||||||
|  | 
 | ||||||
|  | 	assert.True(t, checkSignature(sha256sig, seed, key, value, epoch)) | ||||||
|  | 	// This should be switched to False after fully deprecating SHA1
 | ||||||
|  | 	assert.True(t, checkSignature(sha1sig, seed, key, value, epoch)) | ||||||
|  | 
 | ||||||
|  | 	assert.False(t, checkSignature(sha256sig, seed, key, "tampered", epoch)) | ||||||
|  | 	assert.False(t, checkSignature(sha1sig, seed, key, "tampered", epoch)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestEncodeAndDecodeAccessToken(t *testing.T) { | func TestEncodeAndDecodeAccessToken(t *testing.T) { | ||||||
| 	const secret = "0123456789abcdefghijklmnopqrstuv" | 	const secret = "0123456789abcdefghijklmnopqrstuv" | ||||||
| 	const token = "my access token" | 	const token = "my access token" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue