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)
|
||||
}
|
||||
|
||||
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) {
|
||||
token, err := parseJwtToken(cfg, jwtString)
|
||||
|
||||
|
|
@ -56,7 +63,8 @@ func parseJwtTokenWithRemoteKey(cfg *config.Config, jwtToken string) (*jwt.Token
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return jwt.Parse(jwtToken, jwksVerifier.Keyfunc, jwt.WithAudience(cfg.AuthJwtAud))
|
||||
opts := parserOptionsWithAudience(cfg)
|
||||
return jwt.Parse(jwtToken, jwksVerifier.Keyfunc, opts...)
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -148,24 +156,30 @@ func parseJwtTokenWithLocalKey(cfg *config.Config, jwtString string) (*jwt.Token
|
|||
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 {
|
||||
return nil, fmt.Errorf("parseJwt expected token algorithm RSA but got: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
return pubKey, nil
|
||||
})
|
||||
}
|
||||
|
||||
opts := parserOptionsWithAudience(cfg)
|
||||
return jwt.Parse(jwtString, keyFunc, opts...)
|
||||
}
|
||||
|
||||
// Hash-based Message Authentication Code
|
||||
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 {
|
||||
return nil, fmt.Errorf("parseJwt expected token algorithm HMAC but got: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
return []byte(cfg.AuthJwtHmacSecret), nil
|
||||
})
|
||||
}
|
||||
|
||||
opts := parserOptionsWithAudience(cfg)
|
||||
return jwt.Parse(jwtString, keyFunc, opts...)
|
||||
}
|
||||
|
||||
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 {
|
||||
return createJWTTokenWithExpirationAndAudience(t, privateKey, expire, "")
|
||||
}
|
||||
|
||||
func createJWTTokenWithExpirationAndAudience(t *testing.T, privateKey *rsa.PrivateKey, expire int64, audience string) string {
|
||||
token := jwt.New(jwt.SigningMethodRS256)
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
claims["nbf"] = time.Now().Unix() - 1000
|
||||
claims["exp"] = time.Now().Unix() + expire
|
||||
claims["sub"] = "test"
|
||||
claims["olivetinGroup"] = "test"
|
||||
if audience != "" {
|
||||
claims["aud"] = audience
|
||||
}
|
||||
|
||||
tokenStr, err := token.SignedString(privateKey)
|
||||
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) {
|
||||
testJwkValidationWithAudience(t, expire, expectCode, "", "")
|
||||
}
|
||||
|
||||
func testJwkValidationWithAudience(t *testing.T, expire int64, expectCode int, configAudience, tokenAudience string) {
|
||||
privateKey, publicKeyPath := createKeys(t)
|
||||
defer os.Remove(publicKeyPath)
|
||||
|
||||
|
|
@ -116,8 +127,9 @@ func testJwkValidation(t *testing.T, expire int64, expectCode int) {
|
|||
cfg.AuthJwtClaimUsername = "sub"
|
||||
cfg.AuthJwtClaimUserGroup = "olivetinGroup"
|
||||
cfg.AuthJwtHeader = "Authorization"
|
||||
cfg.AuthJwtAud = configAudience
|
||||
|
||||
tokenStr := createJWTTokenWithExpiration(t, privateKey, expire)
|
||||
tokenStr := createJWTTokenWithExpirationAndAudience(t, privateKey, expire, tokenAudience)
|
||||
handler := setupJWTTestHandler(t, cfg)
|
||||
|
||||
srv := httptest.NewServer(handler)
|
||||
|
|
@ -135,6 +147,14 @@ func TestJWTSignatureVerificationFails(t *testing.T) {
|
|||
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 {
|
||||
token := jwt.New(jwt.SigningMethodRS256)
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
|
|
|
|||
Loading…
Reference in New Issue