From a7b02935e4fefa40f175f4d2143ec9c88a5f90f5 Mon Sep 17 00:00:00 2001 From: Jason Del Ponte <961963+jasdel@users.noreply.github.com> Date: Wed, 3 Nov 2021 12:19:17 -0700 Subject: [PATCH] aws/signer/v4: Fix Signer not trimming header value spaces (#4141) Fixes the AWS Sigv4 signer to trim header value's whitespace when computing the canonical headers block of the string to sign. --- CHANGELOG_PENDING.md | 2 ++ aws/signer/v4/v4.go | 18 +++++++++------ aws/signer/v4/v4_test.go | 47 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 8a1927a39ca..79402172759 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -3,3 +3,5 @@ ### SDK Enhancements ### SDK Bugs +* `aws/signer/v4`: Fix Signer not trimming header value spaces + * Fixes the AWS Sigv4 signer to trim header value's whitespace when computing the canonical headers block of the string to sign. diff --git a/aws/signer/v4/v4.go b/aws/signer/v4/v4.go index d4653031f15..4d78162c034 100644 --- a/aws/signer/v4/v4.go +++ b/aws/signer/v4/v4.go @@ -634,21 +634,25 @@ func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) { ctx.Query.Set("X-Amz-SignedHeaders", ctx.signedHeaders) } - headerValues := make([]string, len(headers)) + headerItems := make([]string, len(headers)) for i, k := range headers { if k == "host" { if ctx.Request.Host != "" { - headerValues[i] = "host:" + ctx.Request.Host + headerItems[i] = "host:" + ctx.Request.Host } else { - headerValues[i] = "host:" + ctx.Request.URL.Host + headerItems[i] = "host:" + ctx.Request.URL.Host } } else { - headerValues[i] = k + ":" + - strings.Join(ctx.SignedHeaderVals[k], ",") + headerValues := make([]string, len(ctx.SignedHeaderVals[k])) + for i, v := range ctx.SignedHeaderVals[k] { + headerValues[i] = strings.TrimSpace(v) + } + headerItems[i] = k + ":" + + strings.Join(headerValues, ",") } } - stripExcessSpaces(headerValues) - ctx.canonicalHeaders = strings.Join(headerValues, "\n") + stripExcessSpaces(headerItems) + ctx.canonicalHeaders = strings.Join(headerItems, "\n") } func (ctx *signingCtx) buildCanonicalString() { diff --git a/aws/signer/v4/v4_test.go b/aws/signer/v4/v4_test.go index a6983d8087b..124d7fa532a 100644 --- a/aws/signer/v4/v4_test.go +++ b/aws/signer/v4/v4_test.go @@ -708,6 +708,53 @@ func TestRequestHost(t *testing.T) { } } +func TestSign_buildCanonicalHeaders(t *testing.T) { + serviceName := "mockAPI" + region := "mock-region" + endpoint := "https://" + serviceName + "." + region + ".amazonaws.com" + + req, err := http.NewRequest("POST", endpoint, nil) + if err != nil { + t.Fatalf("failed to create request, %v", err) + } + + req.Header.Set("FooInnerSpace", " inner space ") + req.Header.Set("FooLeadingSpace", " leading-space") + req.Header.Add("FooMultipleSpace", "no-space") + req.Header.Add("FooMultipleSpace", "\ttab-space") + req.Header.Add("FooMultipleSpace", "trailing-space ") + req.Header.Set("FooNoSpace", "no-space") + req.Header.Set("FooTabSpace", "\ttab-space\t") + req.Header.Set("FooTrailingSpace", "trailing-space ") + req.Header.Set("FooWrappedSpace", " wrapped-space ") + + ctx := &signingCtx{ + ServiceName: serviceName, + Region: region, + Request: req, + Body: nil, + Query: req.URL.Query(), + Time: time.Now(), + ExpireTime: 5 * time.Second, + } + + ctx.buildCanonicalHeaders(ignoredHeaders, ctx.Request.Header) + + expectCanonicalHeaders := strings.Join([]string{ + `fooinnerspace:inner space`, + `fooleadingspace:leading-space`, + `foomultiplespace:no-space,tab-space,trailing-space`, + `foonospace:no-space`, + `footabspace:tab-space`, + `footrailingspace:trailing-space`, + `foowrappedspace:wrapped-space`, + `host:mockAPI.mock-region.amazonaws.com`, + }, "\n") + if e, a := expectCanonicalHeaders, ctx.canonicalHeaders; e != a { + t.Errorf("expect:\n%s\n\nactual:\n%s", e, a) + } +} + func BenchmarkPresignRequest(b *testing.B) { signer := buildSigner() req, body := buildRequestReaderSeeker("dynamodb", "us-east-1", "{}")