fix: use constant-time comparisons for SigV4 signatures

AWS SigV4 signatures are attacker-controlled inputs compared against
server-computed HMAC values. Ordinary string comparison exits at the first
differing byte, which can expose the length of the matching prefix through
response timing and, in principle, enable signature forgery for a fixed request
after many probes.

Use the shared sigv4auth.SecureCompare helper for browser POST-policy signatures
and streaming chunk and trailer signatures. The helper preserves the existing
accept/reject behavior, including rejecting malformed or different-length
signatures, while using crypto/subtle.ConstantTimeCompare for equal-length
values.
This commit is contained in:
Ben McClelland
2026-09-03 09:24:22 -07:00
parent 4a22b6d9d7
commit c7f8bc0ab5
2 changed files with 4 additions and 3 deletions
+1 -1
View File
@@ -189,7 +189,7 @@ func AuthorizePostObject(root RootUserConfig, iam auth.IAMService, region string
return err
}
if expectedSig != signatureHex {
if !sigv4auth.SecureCompare(expectedSig, signatureHex) {
debuglogger.Logf("POST object signature mismatch: expected %s got %s", expectedSig, signatureHex)
// The String to sign for POST request is the base64 encoded policy
// For POST incorrect signature no canonical request and canonical request bytes are returned
+3 -2
View File
@@ -32,6 +32,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/versity/versitygw/debuglogger"
"github.com/versity/versitygw/internal/sigv4auth"
"github.com/versity/versitygw/s3err"
)
@@ -235,7 +236,7 @@ func (cr *ChunkReader) verifyTrailerSignature() error {
strToSign := cr.getTrailerChunkStringToSign()
sig := hex.EncodeToString(hmac256(cr.signingKey, []byte(strToSign)))
if sig != cr.trailerSig {
if !sigv4auth.SecureCompare(sig, cr.trailerSig) {
debuglogger.Logf("incorrect trailing signature: (calculated): %v, (got): %v", sig, cr.trailerSig)
return s3err.GetSignatureDoesNotMatchErr(cr.accessKey, strToSign, cr.trailerSig, HexBytes(strToSign), cr.canonicalString, HexBytes(cr.canonicalString))
}
@@ -262,7 +263,7 @@ func (cr *ChunkReader) checkSignature() error {
cr.chunkHash.Reset()
cr.prevSig = hex.EncodeToString(hmac256(cr.signingKey, []byte(sigstr)))
if cr.prevSig != cr.parsedSig {
if !sigv4auth.SecureCompare(cr.prevSig, cr.parsedSig) {
debuglogger.Logf("incorrect signature: (calculated): %v, (got) %v", cr.prevSig, cr.parsedSig)
return s3err.GetSignatureDoesNotMatchErr(cr.accessKey, sigstr, cr.parsedSig, HexBytes(sigstr), cr.canonicalString, HexBytes(cr.canonicalString))
}