security: GHSA-g962-2j28-3cg9 (HIGH) JWT Audience Validation Bypass in Local Key and HMAC Modes
This commit is contained in:
parent
92a1346edf
commit
e97d8ecbd8
|
|
@ -33,6 +33,13 @@ func parseJwtToken(cfg *config.Config, jwtString string) (*jwt.Token, error) {
|
||||||
return parseJwtTokenWithHMAC(cfg, jwtString)
|
return parseJwtTokenWithHMAC(cfg, jwtString)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parserOptionsWithAudience(cfg *config.Config) []jwt.ParserOption {
|
||||||
|
if cfg.AuthJwtAud == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []jwt.ParserOption{jwt.WithAudience(cfg.AuthJwtAud)}
|
||||||
|
}
|
||||||
|
|
||||||
func getClaimsFromJwtToken(cfg *config.Config, jwtString string) (jwt.MapClaims, error) {
|
func getClaimsFromJwtToken(cfg *config.Config, jwtString string) (jwt.MapClaims, error) {
|
||||||
token, err := parseJwtToken(cfg, jwtString)
|
token, err := parseJwtToken(cfg, jwtString)
|
||||||
|
|
||||||
|
|
@ -56,7 +63,8 @@ func parseJwtTokenWithRemoteKey(cfg *config.Config, jwtToken string) (*jwt.Token
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return jwt.Parse(jwtToken, jwksVerifier.Keyfunc, jwt.WithAudience(cfg.AuthJwtAud))
|
opts := parserOptionsWithAudience(cfg)
|
||||||
|
return jwt.Parse(jwtToken, jwksVerifier.Keyfunc, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -148,24 +156,30 @@ func parseJwtTokenWithLocalKey(cfg *config.Config, jwtString string) (*jwt.Token
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return jwt.Parse(jwtString, func(token *jwt.Token) (interface{}, error) {
|
keyFunc := func(token *jwt.Token) (interface{}, error) {
|
||||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||||
return nil, fmt.Errorf("parseJwt expected token algorithm RSA but got: %v", token.Header["alg"])
|
return nil, fmt.Errorf("parseJwt expected token algorithm RSA but got: %v", token.Header["alg"])
|
||||||
}
|
}
|
||||||
|
|
||||||
return pubKey, nil
|
return pubKey, nil
|
||||||
})
|
}
|
||||||
|
|
||||||
|
opts := parserOptionsWithAudience(cfg)
|
||||||
|
return jwt.Parse(jwtString, keyFunc, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash-based Message Authentication Code
|
// Hash-based Message Authentication Code
|
||||||
func parseJwtTokenWithHMAC(cfg *config.Config, jwtString string) (*jwt.Token, error) {
|
func parseJwtTokenWithHMAC(cfg *config.Config, jwtString string) (*jwt.Token, error) {
|
||||||
return jwt.Parse(jwtString, func(token *jwt.Token) (interface{}, error) {
|
keyFunc := func(token *jwt.Token) (interface{}, error) {
|
||||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||||
return nil, fmt.Errorf("parseJwt expected token algorithm HMAC but got: %v", token.Header["alg"])
|
return nil, fmt.Errorf("parseJwt expected token algorithm HMAC but got: %v", token.Header["alg"])
|
||||||
}
|
}
|
||||||
|
|
||||||
return []byte(cfg.AuthJwtHmacSecret), nil
|
return []byte(cfg.AuthJwtHmacSecret), nil
|
||||||
})
|
}
|
||||||
|
|
||||||
|
opts := parserOptionsWithAudience(cfg)
|
||||||
|
return jwt.Parse(jwtString, keyFunc, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupClaimValueOrDefault(claims jwt.MapClaims, key string, def string) string {
|
func lookupClaimValueOrDefault(claims jwt.MapClaims, key string, def string) string {
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,19 @@ func newMux() *http.ServeMux {
|
||||||
}
|
}
|
||||||
|
|
||||||
func createJWTTokenWithExpiration(t *testing.T, privateKey *rsa.PrivateKey, expire int64) string {
|
func createJWTTokenWithExpiration(t *testing.T, privateKey *rsa.PrivateKey, expire int64) string {
|
||||||
|
return createJWTTokenWithExpirationAndAudience(t, privateKey, expire, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createJWTTokenWithExpirationAndAudience(t *testing.T, privateKey *rsa.PrivateKey, expire int64, audience string) string {
|
||||||
token := jwt.New(jwt.SigningMethodRS256)
|
token := jwt.New(jwt.SigningMethodRS256)
|
||||||
claims := token.Claims.(jwt.MapClaims)
|
claims := token.Claims.(jwt.MapClaims)
|
||||||
claims["nbf"] = time.Now().Unix() - 1000
|
claims["nbf"] = time.Now().Unix() - 1000
|
||||||
claims["exp"] = time.Now().Unix() + expire
|
claims["exp"] = time.Now().Unix() + expire
|
||||||
claims["sub"] = "test"
|
claims["sub"] = "test"
|
||||||
claims["olivetinGroup"] = "test"
|
claims["olivetinGroup"] = "test"
|
||||||
|
if audience != "" {
|
||||||
|
claims["aud"] = audience
|
||||||
|
}
|
||||||
|
|
||||||
tokenStr, err := token.SignedString(privateKey)
|
tokenStr, err := token.SignedString(privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -108,6 +115,10 @@ func verifyJWTResponse(t *testing.T, res *http.Response, expectCode int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testJwkValidation(t *testing.T, expire int64, expectCode int) {
|
func testJwkValidation(t *testing.T, expire int64, expectCode int) {
|
||||||
|
testJwkValidationWithAudience(t, expire, expectCode, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testJwkValidationWithAudience(t *testing.T, expire int64, expectCode int, configAudience, tokenAudience string) {
|
||||||
privateKey, publicKeyPath := createKeys(t)
|
privateKey, publicKeyPath := createKeys(t)
|
||||||
defer os.Remove(publicKeyPath)
|
defer os.Remove(publicKeyPath)
|
||||||
|
|
||||||
|
|
@ -116,8 +127,9 @@ func testJwkValidation(t *testing.T, expire int64, expectCode int) {
|
||||||
cfg.AuthJwtClaimUsername = "sub"
|
cfg.AuthJwtClaimUsername = "sub"
|
||||||
cfg.AuthJwtClaimUserGroup = "olivetinGroup"
|
cfg.AuthJwtClaimUserGroup = "olivetinGroup"
|
||||||
cfg.AuthJwtHeader = "Authorization"
|
cfg.AuthJwtHeader = "Authorization"
|
||||||
|
cfg.AuthJwtAud = configAudience
|
||||||
|
|
||||||
tokenStr := createJWTTokenWithExpiration(t, privateKey, expire)
|
tokenStr := createJWTTokenWithExpirationAndAudience(t, privateKey, expire, tokenAudience)
|
||||||
handler := setupJWTTestHandler(t, cfg)
|
handler := setupJWTTestHandler(t, cfg)
|
||||||
|
|
||||||
srv := httptest.NewServer(handler)
|
srv := httptest.NewServer(handler)
|
||||||
|
|
@ -135,6 +147,14 @@ func TestJWTSignatureVerificationFails(t *testing.T) {
|
||||||
testJwkValidation(t, -500, 403)
|
testJwkValidation(t, -500, 403)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJWTAudienceValidationRejectsWrongAudience(t *testing.T) {
|
||||||
|
testJwkValidationWithAudience(t, 1000, 403, "expected-audience", "wrong-audience")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJWTAudienceValidationAcceptsCorrectAudience(t *testing.T) {
|
||||||
|
testJwkValidationWithAudience(t, 1000, 200, "expected-audience", "expected-audience")
|
||||||
|
}
|
||||||
|
|
||||||
func createJWTTokenWithGroups(t *testing.T, privateKey *rsa.PrivateKey, groups interface{}) string {
|
func createJWTTokenWithGroups(t *testing.T, privateKey *rsa.PrivateKey, groups interface{}) string {
|
||||||
token := jwt.New(jwt.SigningMethodRS256)
|
token := jwt.New(jwt.SigningMethodRS256)
|
||||||
claims := token.Claims.(jwt.MapClaims)
|
claims := token.Claims.(jwt.MapClaims)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue