Skip to content

Commit 237c0a7

Browse files
committed
feat(auth): implement constant-time secret key validation
1 parent fa0a132 commit 237c0a7

1 file changed

Lines changed: 27 additions & 10 deletions

File tree

internal/features/auth/service.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package auth
22

33
import (
44
"context"
5+
"crypto/sha256"
6+
"crypto/subtle"
57
"fmt"
68
"net/http"
79
"strings"
@@ -100,11 +102,8 @@ func (s *Service) GetAuthorizationFromBearerToken(_ context.Context, headerToken
100102

101103
token := parts[1]
102104

103-
if token != s.secretKey {
104-
return "", &api.HTTPError{
105-
Message: "Invalid token",
106-
StatusCode: http.StatusUnauthorized,
107-
}
105+
if err := s.validateSecretKey(token); err != nil {
106+
return "", err
108107
}
109108

110109
return token, nil
@@ -118,11 +117,8 @@ func (s *Service) GetAuthorizationFromQuery(_ context.Context, queryToken string
118117
}
119118
}
120119

121-
if queryToken != s.secretKey {
122-
return "", &api.HTTPError{
123-
Message: "Invalid token",
124-
StatusCode: http.StatusUnauthorized,
125-
}
120+
if err := s.validateSecretKey(queryToken); err != nil {
121+
return "", err
126122
}
127123

128124
return queryToken, nil
@@ -152,3 +148,24 @@ func (s *Service) IsAdminFromOauth(ctx context.Context) (bool, error) {
152148

153149
return true, nil
154150
}
151+
152+
// Compare the token against the secret in constant time to avoid leaking
153+
// information via timing differences. We hash both values first so the
154+
// comparison runs over fixed-length (32-byte) inputs; subtle.ConstantTimeCompare
155+
// would otherwise short-circuit on a length mismatch and leak the secret's length.
156+
//
157+
// Rate limiting (Cloudflare + API Gateway) is the primary defense against
158+
// brute-force/timing attacks; this is defense-in-depth in case those fail open.
159+
func (s *Service) validateSecretKey(token string) error {
160+
tokenHash := sha256.Sum256([]byte(token))
161+
secretKeyHash := sha256.Sum256([]byte(s.secretKey))
162+
163+
if subtle.ConstantTimeCompare(tokenHash[:], secretKeyHash[:]) != 1 {
164+
return &api.HTTPError{
165+
Message: "Invalid token",
166+
StatusCode: http.StatusUnauthorized,
167+
}
168+
}
169+
170+
return nil
171+
}

0 commit comments

Comments
 (0)