jcberquist / aws-cfml

Lucee/ColdFusion library for interacting with AWS API's
MIT License
72 stars 53 forks source link

Signature fails when path is URL encoded with CF-2016 #18

Closed doodi-v1 closed 5 years ago

doodi-v1 commented 5 years ago

I was fighting with a AWS signature does not match issue and narrowed it down to a specific line in coldfusion.cfc:20. Original line is:

var fullPath = utils.encodeUrl( path, false ) & ( !queryParams.isEmpty() ? ( '?' & utils.parseQueryParams( queryParams ) ) : '' );

Changing to:

var fullPath = path & ( !queryParams.isEmpty() ? ( '?' & utils.parseQueryParams( queryParams ) ) : '' );

fixes the issue.

I was posting a request to the execute-api service at AWS. My URI is: https://someletters.execute-api.us-west-2.amazonaws.com/beta/@connections/a0abIc59PHcCJQQ=

doodi-v1 commented 5 years ago

Two issues in the URI caused the signature to fail: the @ symbol and the trailing = symbol. AWS receives these correctly with the change. Without the change, AWS double encodes the character.

jcberquist commented 5 years ago

This is interesting. @ and = are both reserved characters and so I would expect them to be encoded in the URL. With the change you note above, ACF does not encode those characters. When I test paths with those characters, encoding them seems to work. I have not tested the execute-api service, however. Could you say more about what you mean by "AWS double encoded the character"?

doodi-v1 commented 5 years ago

Here is the original error response from AWS that I was receiving (luckily, they do a good job of documenting the failure in the message):

The Canonical String for this request should have been 'POST /beta/%2540connections/ax04CfGcPHcCJkg%253D

host:sometext.execute-api.us-west-2.amazonaws.com\nx-amz-date:20190605T175310Z\n\nhost;x-amz-date\n9779345914b767f358db1224bd6bfa2fd15c1ba52d2ecadf3fca3670935e021b'

Note the %2540 and %253D - double-URL encoded @ and =

doodi-v1 commented 5 years ago

I've been told by my IT that one of the last CF updates greatly increased CF URL encoding - tags that didn't URL encode were now encoding automatically. Maybe it's a CF version issue.

doodi-v1 commented 5 years ago

By the way, great job with the library! I would still be researching how to properly sign AWS requests if I did not have your code as a starting point. Kudos!!!

jcberquist commented 5 years ago

I do wonder if it is an issue with a specific version of ACF (or I am just missing something obvious 😄) - I can't find a version so far that double encodes the URL. I have tried passing URLs with %40 and %3D in them into cfhttp in various ACF2016 versions and I can't get it to request %2540 and %253D. Would it be possible to know the exact 2016 version you are on? Also just to confirm, your original post mentioned api.cfc:20 - did you mean coldfusion.cfc:20? And thank you for the kind words!

doodi-v1 commented 5 years ago

Server Details

Server Product | ColdFusion 2016 Version | 2016.0.10.314028 Tomcat Version | 8.5.32.0 Edition | Developer Operating System | Windows 10 OS Version | 10.0 Update Level | C:/ColdFusion2016/cfusion/lib/updates/chf20160010.jar Adobe Driver Version | 5.1.4 (Build 0001)

I'm not 100% sure, but I'm guessing the latest 2018 version will act the same. I have yet to find any discrepancies between the latest 2016 and latest 2018 on functionality like this (cfhttp and url encoding).

And yes, you are correct, coldfusion.cfc:20, not api.cfc.

jcberquist commented 5 years ago

Thank you for all of the information, it has been very helpful. I spent a little while digging into this today, and now I think I know what is going on. There is a difference between S3 and other services with regard to Signature V4 request signing, specifically with regard to how a canonical request is constructed. According to the docs:

Normalize URI paths according to RFC 3986. Remove redundant and relative path components. Each path segment must be URI-encoded twice (except for Amazon S3 which only gets URI-encoded once).

The key is that "each path segment must be URI-encoded twice". Now in most cases that won't make a difference, but it does with your example URL above - and I couldn't reproduce with S3 URLs since they behave differently. But today I could reproduce it using the API Gateway with both ACF and Lucee - the cfhttp requests are behaving correctly; the issue is that I am not constructing non S3 canonical requests correctly.

I really appreciate you raising the issue!

doodi-v1 commented 5 years ago

Very strange. I'm actually surprised AWS didn't catch the discrepancy and correct it on their end. Sounds like they need a case opened. I'm in awe at the level you went to track down the root cause. Need a job? ;-)

jcberquist commented 5 years ago

Alright, I have pushed v1.7.1 that includes the above commit. I believe it will fix this issue. Please let me know!

doodi-v1 commented 5 years ago

Quick test passed. My demo app only tests a call to s3.listBucket and an execute-api call and both worked as expected. Thank you.

jcberquist commented 5 years ago

Great, glad to hear it. I had to switch from using URLEncodedFormat() to encodeForURL() due to a recent issue introduced (it makes double encoding using URLEncodedFormat impossible): CF-4204127 CF-4204045 With that change, however, things do seem to be working properly at the moment.

jcberquist commented 5 years ago

As a heads up, I have now published v1.7.2 to deal with the fact that encodeForURL encodes spaces as +'s and that isn't valid for the non query string part of a URL.

doodi-v1 commented 5 years ago

My same tests were successful - but they don't specifically test the + issue, just that it didn't break the previous fix.

jcberquist commented 5 years ago

Yep, I get that, just wanted to make sure you knew I had pushed a further patch.