smithy-lang / smithy-typescript

Smithy code generators for TypeScript. (in development)
Apache License 2.0
223 stars 84 forks source link

Encode URI according to RFC3986 #1224

Closed zirkelc closed 5 months ago

zirkelc commented 6 months ago

Bug

If the path or query string of an URI contains a * character, the calculated signature on the client differs from the signature AWS calculates on the server. This results in HTTP 403 errors with The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method.

Investigation

The SignatureV4 class and getCanonicalPath method encodes the path only with default encodeURIComponent function:

https://github.com/smithy-lang/smithy-typescript/blob/66f2b660e6b1dfc40699772a51778086495fd2cf/packages/signature-v4/src/SignatureV4.ts#L316-L336

However, the getCanonicalQuery function encodes the query string with the escapeUri function:

https://github.com/smithy-lang/smithy-typescript/blob/66f2b660e6b1dfc40699772a51778086495fd2cf/packages/signature-v4/src/getCanonicalQuery.ts#L9-L37

This escapeUri function encodes the URI according to RFC 3986:

https://github.com/smithy-lang/smithy-typescript/blob/66f2b660e6b1dfc40699772a51778086495fd2cf/packages/util-uri-escape/src/escape-uri.ts#L4-L8

The requirement for RFC 3986 encoded URIs is also described in the official docs

https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html

URI encode every byte. UriEncode() must enforce the following rules:

  • URI encode every byte except the unreserved characters: 'A'-'Z', 'a'-'z', '0'-'9', '-', '.', '_', and '~'.
  • The space character is a reserved character and must be encoded as "%20" (and not as "+").
  • Each URI encoded byte is formed by a '%' and the two-digit hexadecimal value of the byte.
  • Letters in the hexadecimal value must be uppercase, for example "%1A".
  • Encode the forward slash character, '/', everywhere except in the object key name. For example, if the object key name is photos/Jan/sample.jpg, the forward slash in the key name is not encoded.

Important The standard UriEncode functions provided by your development platform may not work because of differences in implementation and related ambiguity in the underlying RFCs. We recommend that you write your own custom UriEncode function to ensure that your encoding will work. To see an example of a UriEncode function in Java, see Java Utilities on the GitHub website.

The last important section refers to the SdkHttpUtils.urlEncode method from the Java SDK:

https://github.com/aws/aws-sdk-java/blob/1b3444aa78f4579c4083bd4b3858322bc343a906/aws-java-sdk-core/src/main/java/com/amazonaws/util/SdkHttpUtils.java#L66-L99

In the Java code, the * will be encoded as %2A. This encoding follow RFC3986 that specifies that the characters ! ' ( ) * must be encoded. See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_rfc3986

Here is also a PR from the Amplify project that implemented RFC 3986 encoding for the query string: https://github.com/aws-amplify/amplify-js/pull/2631

Conclusion

This issue can of course be mitigated if the requester encodes the URI on their own according to extendedURIEncode function. However, to be consistent with other AWS SDKs and fully compliant with the SignatureV4 spec, I think this part should be handled by the SignatureV4 class and not by the requester.

As there is also a escapeUriPath function, I would suggest adding to SignatureV4.getCanonicalPath:

https://github.com/smithy-lang/smithy-typescript/blob/66f2b660e6b1dfc40699772a51778086495fd2cf/packages/util-uri-escape/src/escape-uri-path.ts#L6C14-L6C27

ricksterhd123 commented 5 months ago

try your hand at a PR :)

kuhe commented 5 months ago

which AWS service and operation did you encounter this with?

zirkelc commented 5 months ago

I have two people reporting this as issue when using Amazon Open Search and path contains a * character. YOu will find more details here: https://github.com/zirkelc/aws-sigv4-fetch/issues/10

I myself could reproduce this issue with AWS API Gateway.

kuhe commented 5 months ago

The fix PR was released. It's not enforced as the minimum version in SDK packages yet, but you can get it via the ^x.y.z range that AWS SDKs use for @smithy/* packages if you update your package lockfile.