diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-3312616.json b/.changes/next-release/bugfix-AWSSDKforJavav2-3312616.json new file mode 100644 index 000000000000..cdfb6f4f97d9 --- /dev/null +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-3312616.json @@ -0,0 +1,6 @@ +{ + "category": "AWS SDK for Java v2", + "contributor": "", + "type": "bugfix", + "description": "Update Sigv4a signer to include the port in the Host header, when the port does not match the standard port for the protocol. This allows requests to endpoints with non-standard ports to succeed." +} diff --git a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/SigningUtils.java b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/SigningUtils.java index 24cf1e0d98a5..6541997d5b32 100644 --- a/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/SigningUtils.java +++ b/core/auth-crt/src/main/java/software/amazon/awssdk/authcrt/signer/internal/SigningUtils.java @@ -15,8 +15,6 @@ package software.amazon.awssdk.authcrt.signer.internal; -import static software.amazon.awssdk.utils.CollectionUtils.isNullOrEmpty; - import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Duration; @@ -35,6 +33,7 @@ import software.amazon.awssdk.crt.auth.credentials.Credentials; import software.amazon.awssdk.http.SdkHttpFullRequest; import software.amazon.awssdk.utils.StringUtils; +import software.amazon.awssdk.utils.http.SdkHttpUtils; @SdkInternalApi public class SigningUtils { @@ -116,19 +115,19 @@ public static SdkHttpFullRequest sanitizeSdkRequestForCrtSigning(SdkHttpFullRequ builder.clearHeaders(); - // Add host if missing - Map> headers = request.headers(); - if (isNullOrEmpty(headers.get(HOST_HEADER))) { - builder.putHeader(HOST_HEADER, request.host()); - } - // Filter headers that will cause signing to fail - for (Map.Entry> header: headers.entrySet()) { + for (Map.Entry> header: request.headers().entrySet()) { if (!FORBIDDEN_HEADERS.contains(header.getKey())) { builder.putHeader(header.getKey(), header.getValue()); } } + // Add host, which must be signed. We ignore any pre-existing Host header to match the behavior of the SigV4 signer. + String hostHeader = SdkHttpUtils.isUsingStandardPort(request.protocol(), request.port()) + ? request.host() + : request.host() + ":" + request.port(); + builder.putHeader(HOST_HEADER, hostHeader); + builder.clearQueryParameters(); // Filter query parameters that will cause signing to fail diff --git a/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSignerTest.java b/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSignerTest.java index 3b936d9cf7b5..59cb8235b429 100644 --- a/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSignerTest.java +++ b/core/auth-crt/src/test/java/software/amazon/awssdk/authcrt/signer/AwsCrtV4aSignerTest.java @@ -44,6 +44,45 @@ public void setup() { v4aSigner = AwsCrtV4aSigner.create(); } + @Test + public void hostHeaderExcludesStandardHttpPort() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); + + SdkHttpFullRequest request = testCase.requestBuilder.protocol("http") + .port(80) + .build(); + SdkHttpFullRequest signed = v4aSigner.sign(request, executionAttributes); + + assertThat(signed.firstMatchingHeader("Host")).hasValue("demo.us-east-1.amazonaws.com"); + } + + @Test + public void hostHeaderExcludesStandardHttpsPort() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); + + SdkHttpFullRequest request = testCase.requestBuilder.protocol("https") + .port(443) + .build(); + SdkHttpFullRequest signed = v4aSigner.sign(request, executionAttributes); + + assertThat(signed.firstMatchingHeader("Host")).hasValue("demo.us-east-1.amazonaws.com"); + } + + @Test + public void hostHeaderIncludesNonStandardPorts() { + SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase(); + ExecutionAttributes executionAttributes = SignerTestUtils.buildBasicExecutionAttributes(testCase); + + SdkHttpFullRequest request = testCase.requestBuilder.protocol("http") + .port(443) + .build(); + SdkHttpFullRequest signed = v4aSigner.sign(request, executionAttributes); + + assertThat(signed.firstMatchingHeader("Host")).hasValue("demo.us-east-1.amazonaws.com:443"); + } + @Test public void testHeaderSigning() { SigningTestCase testCase = SignerTestUtils.createBasicHeaderSigningTestCase();