mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Auth: Omit all base64 paddings in JWT tokens for the JWT auth (#35602)
Omitting all base64 paddings (=) in JWT tokens. Fixes #34496
This commit is contained in:
parent
00ffe1a4fd
commit
7c5de96503
@ -2,7 +2,9 @@ package jwt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
@ -55,9 +57,18 @@ type AuthService struct {
|
||||
expectRegistered jwt.Expected
|
||||
}
|
||||
|
||||
// Sanitize JWT base64 strings to remove paddings everywhere
|
||||
func sanitizeJWT(jwtToken string) string {
|
||||
// JWT can be compact, JSON flatened or JSON general
|
||||
// In every cases, parts are base64 strings without padding
|
||||
// The padding char (=) should never interfer with data
|
||||
return strings.ReplaceAll(jwtToken, string(base64.StdPadding), "")
|
||||
}
|
||||
|
||||
func (s *AuthService) Verify(ctx context.Context, strToken string) (models.JWTClaims, error) {
|
||||
s.log.Debug("Parsing JSON Web Token")
|
||||
|
||||
strToken = sanitizeJWT(strToken)
|
||||
token, err := jwt.ParseSigned(strToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -3,12 +3,14 @@ package jwt
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -35,9 +37,9 @@ type configureFunc func(*testing.T, *setting.Cfg)
|
||||
type scenarioFunc func(*testing.T, scenarioContext)
|
||||
type cachingScenarioFunc func(*testing.T, cachingScenarioContext)
|
||||
|
||||
func TestVerifyUsingPKIXPublicKeyFile(t *testing.T) {
|
||||
subject := "foo-subj"
|
||||
const subject = "foo-subj"
|
||||
|
||||
func TestVerifyUsingPKIXPublicKeyFile(t *testing.T) {
|
||||
key := rsaKeys[0]
|
||||
unknownKey := rsaKeys[1]
|
||||
|
||||
@ -77,8 +79,6 @@ func TestVerifyUsingJWKSetFile(t *testing.T) {
|
||||
cfg.JWTAuthJWKSetFile = file.Name()
|
||||
}
|
||||
|
||||
subject := "foo-subj"
|
||||
|
||||
scenario(t, "verifies a token signed with a key from the set", func(t *testing.T, sc scenarioContext) {
|
||||
token := sign(t, &jwKeys[0], jwt.Claims{Subject: subject})
|
||||
verifiedClaims, err := sc.authJWTSvc.Verify(sc.ctx, token)
|
||||
@ -101,8 +101,6 @@ func TestVerifyUsingJWKSetFile(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestVerifyUsingJWKSetURL(t *testing.T) {
|
||||
subject := "foo-subj"
|
||||
|
||||
t.Run("should refuse to start with non-https URL", func(t *testing.T) {
|
||||
var err error
|
||||
|
||||
@ -139,8 +137,6 @@ func TestVerifyUsingJWKSetURL(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCachingJWKHTTPResponse(t *testing.T) {
|
||||
subject := "foo-subj"
|
||||
|
||||
jwkCachingScenario(t, "caches the jwk response", func(t *testing.T, sc cachingScenarioContext) {
|
||||
for i := 0; i < 5; i++ {
|
||||
token := sign(t, &jwKeys[0], jwt.Claims{Subject: subject})
|
||||
@ -359,6 +355,25 @@ func jwkCachingScenario(t *testing.T, desc string, fn cachingScenarioFunc, cbs .
|
||||
})
|
||||
}
|
||||
|
||||
func TestBase64Paddings(t *testing.T) {
|
||||
key := rsaKeys[0]
|
||||
|
||||
scenario(t, "verifies a token with base64 padding (non compliant rfc7515#section-2 but accepted)", func(t *testing.T, sc scenarioContext) {
|
||||
token := sign(t, key, jwt.Claims{
|
||||
Subject: subject,
|
||||
})
|
||||
var tokenParts []string
|
||||
for i, part := range strings.Split(token, ".") {
|
||||
// Create parts with different padding numbers to test multiple cases.
|
||||
tokenParts = append(tokenParts, part+strings.Repeat(string(base64.StdPadding), i))
|
||||
}
|
||||
token = strings.Join(tokenParts, ".")
|
||||
verifiedClaims, err := sc.authJWTSvc.Verify(sc.ctx, token)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, verifiedClaims["sub"], subject)
|
||||
}, configurePKIXPublicKeyFile)
|
||||
}
|
||||
|
||||
func scenario(t *testing.T, desc string, fn scenarioFunc, cbs ...configureFunc) {
|
||||
t.Helper()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user