danieljoos/aws-sign-web

URLs with percent (%) encoded values fails signature check

Closed this issue · 10 comments

URLs with percent (%) encoded values get decoded and fail to match signature.

This snippet in SimpleUriParser decodes the path but doesn't encode it initially. This results in any URL-encoded characters to be un-encoded. For example, %20 is changed to a space.

return function (uri) {
parser.href = uri;
return {
      protocol: parser.protocol,
      host: parser.host.replace(/^(.*):((80)|(443))$/, '$1'),
      path: ((parser.pathname.charAt(0) !== '/') ? '/' : '') +
              decodeURIComponent(parser.pathname),
      queryParams: extractQueryParams(parser.search)
      };
};

A solution is to encode the uri parameter, as shown below.

return function (uri) {
parser.href = **encodeURI**(uri);
return {
     protocol: parser.protocol,
     host: parser.host.replace(/^(.*):((80)|(443))$/, '$1'),
     path: ((parser.pathname.charAt(0) !== '/') ? '/' : '') +
          decodeURIComponent(parser.pathname),
     queryParams: extractQueryParams(parser.search)
      };
};

Hi,
I think the URI parser implementation is correct here.
I was trying to reproduce this by using the input URL /percent%20url. The parser results with /percent url (as you already noticed). But this is fine. During the creation of the canonical request, the path gets URI encoded again and results in /percent%20url.

Could you maybe give an example where the current implementation fails for you? I'll try to create a test-case for it and see if it fails here as well.

Cheers,
Daniel

Will do.

Finally had some cycles to look at this. Your algorithm looks correct, but is failing in our application. I have a support ticket open with Amazon to help clarify the problem. The issue definitely surrounds whether the canonical URI needs to be double encoded (e.g., ":" => "%253A") or single encoded (e.g., ":" => "%3A"). Single encoding would seem correct (as you have it), but AWS API Gateway is failing the request stating the signatures do not match.

Found a solution! The decodeURIComponent calls (x3) in SimpleUriParser need to be decodeURI. This will properly decode the UTF-8 characters without decoding the reserved characters (e.g., /, :, etc).
Is this acceptable? Would you prefer I make a pull-request?

This still sounds kind-of strange to me. Why would AWS want some characters to be double encoded? I didn't find anything about it in the docs (http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html).

For the / it actually makes sense not to decode, because we later split the path using that character as delimiter. We might need to find a solution here.

Do your AWS API endpoints contain slashes /?
Would it be possible to provide me with a URL (Path would even be enough) where AWS API Gateway fails? (Of course, strip-out all private strings/parts and/or replace them with example strings)
Thanks in advance.

Cheers,
Daniel

Update: I just replaced the decodeURIComponent with decodeURI locally and it still seems to pass the official AWS signature test suite. Therefore I think it's ok to do so. It would be great if you could create a PR for it.

Will create a PR. The path is encoded by the client, then the AWS signing algorithm encodes it when making it canonical. The algorithm also encodes the query string parameter values. Unfortunately, the AWS test cases don't include reserved characters.
Our API does not have slashes, but does have colons :.

Testing 1.4.0... Thanks for releasing this new version.

This fixed the problem with : but not +. Don't have a suggestion, but I'll close this issue.

Found that if I encodeURI(url) prior to calling .sign that it worked with v1.4.0. I don't know why. Perhaps something is affected by Angular.