Nodejs-storage: getSignedUrl with cname results in SignatureDoesNotMatch error

Created on 18 Sep 2019  路  14Comments  路  Source: googleapis/nodejs-storage

~Note that this works fine with v2.~ I thought it did, but it actually no longer does.

Environment details

  • OS: macOS 10.14.6
  • Node.js version: v8.16.1
  • npm version: 6.4.1
  • yarn version: 1.17.3
  • @google-cloud/storage version: 3.2.1

Steps to reproduce v2

```js
file.getSignedUrl({
version: 'v2',
action: 'read',
expires: Date.now() + 10000000,
cname: 'https://storage.customhostname.com'
}, (err, url) => {

})


With `cname`, the URL doesn't work in the [browser](https://storage.dev.bookcreator.com/data%2Fi4rNhrOGJgNGlzELwesBcVrSfnG2%2Fbooks%2Fqle8FcNGSNGXQGd1EElspA%2Fresources%2FQC5Qdp5gQgeCMw7AQ18nmw.m4a?GoogleAccessId=api-service%40bookcreator-dev.iam.gserviceaccount.com&Expires=1568829600&Signature=oDAGX4myOoD%2F1UrWPhKOhl2WCw09fQ%2FatkJk5C8%2Bvk4FV%2F5t5lj2Kr3EUNUkQc2jm7vMg1X1XGu8y23sTIklW525f6VbJvN7MjHSGSnh317FO%2Fv6lTBVSekmN7CfUY4Bds%2BdcWYj%2FgI%2FjPFqXMsjmr%2Bqs4wYGRZJ0P6%2BiGUgX8WueF%2F0LNdsI44OsZiZ7z%2FR3vXR%2BTzCfRfxOMETAYrn8jjjITMJpQ1UO7PVYc5E0aAAT0URh816T%2BSee%2Fu3UNkD0hUMJbQC6XtiqPsOawHb%2Bi%2FCNr0R7yjYuluKnIuCe51oIBVQdG9iNTBZD8oJFsPtgrMufibXgS2e%2BuJcTPbMww%3D%3D).

However, sending the same request using `curl` works:

`curl 'https://storage.dev.bookcreator.com/data%2Fi4rNhrOGJgNGlzELwesBcVrSfnG2%2Fbooks%2Fqle8FcNGSNGXQGd1EElspA%2Fresources%2FQC5Qdp5gQgeCMw7AQ18nmw.m4a?GoogleAccessId=api-service%40bookcreator-dev.iam.gserviceaccount.com&Expires=1568829600&Signature=oDAGX4myOoD%2F1UrWPhKOhl2WCw09fQ%2FatkJk5C8%2Bvk4FV%2F5t5lj2Kr3EUNUkQc2jm7vMg1X1XGu8y23sTIklW525f6VbJvN7MjHSGSnh317FO%2Fv6lTBVSekmN7CfUY4Bds%2BdcWYj%2FgI%2FjPFqXMsjmr%2Bqs4wYGRZJ0P6%2BiGUgX8WueF%2F0LNdsI44OsZiZ7z%2FR3vXR%2BTzCfRfxOMETAYrn8jjjITMJpQ1UO7PVYc5E0aAAT0URh816T%2BSee%2Fu3UNkD0hUMJbQC6XtiqPsOawHb%2Bi%2FCNr0R7yjYuluKnIuCe51oIBVQdG9iNTBZD8oJFsPtgrMufibXgS2e%2BuJcTPbMww%3D%3D' -Lv > /dev/null`

But sending the same user-agent as the browser, causes a 403:

`curl 'https://storage.dev.bookcreator.com/data%2Fi4rNhrOGJgNGlzELwesBcVrSfnG2%2Fbooks%2Fqle8FcNGSNGXQGd1EElspA%2Fresources%2FQC5Qdp5gQgeCMw7AQ18nmw.m4a?GoogleAccessId=api-service%40bookcreator-dev.iam.gserviceaccount.com&Expires=1568829600&Signature=oDAGX4myOoD%2F1UrWPhKOhl2WCw09fQ%2FatkJk5C8%2Bvk4FV%2F5t5lj2Kr3EUNUkQc2jm7vMg1X1XGu8y23sTIklW525f6VbJvN7MjHSGSnh317FO%2Fv6lTBVSekmN7CfUY4Bds%2BdcWYj%2FgI%2FjPFqXMsjmr%2Bqs4wYGRZJ0P6%2BiGUgX8WueF%2F0LNdsI44OsZiZ7z%2FR3vXR%2BTzCfRfxOMETAYrn8jjjITMJpQ1UO7PVYc5E0aAAT0URh816T%2BSee%2Fu3UNkD0hUMJbQC6XtiqPsOawHb%2Bi%2FCNr0R7yjYuluKnIuCe51oIBVQdG9iNTBZD8oJFsPtgrMufibXgS2e%2BuJcTPbMww%3D%3D' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' -Lv > /dev/null`

If you change the user-agent to just `user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko)` it also works.

The `v2` signing used to work (~but unsure when it stopped~ it stopped working around 1700 yesterday [GMT]).

#### Steps to reproduce `v4`

I thought that maybe the legacy (as the docs called it) `v2` had been deprecated so tried the `v4` and this doesn't work either.

  ```js
file.getSignedUrl({
   version: 'v4',
   action: 'read',
   expires: Date.now() + 10000000,
   cname: 'https://storage.customhostname.com'
}, (err, url) => {

})

The URL produced does have the correct host, but on going to the URL you get a SignatureDoesNotMatch error.
Removing the cname results in a working URL.

E.g.: with cname:

https://storage.dev.bookcreator.com/data%2Fi4rNhrOGJgNGlzELwesBcVrSfnG2%2Fbooks%2Fqle8FcNGSNGXQGd1EElspA%2Fresources%2FQC5Qdp5gQgeCMw7AQ18nmw.m4a?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=api-service%40bookcreator-dev.iam.gserviceaccount.com%2F20190918%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190918T143035Z&X-Goog-Expires=12565&X-Goog-SignedHeaders=host&X-Goog-Signature=484a09592958f698cb72e534cc82578f439ba6e6d7ceccc3b50e4d89b78afb6e3566bd9e9d1c160213b6c76e30dbc1ff5a6d43a8bb060219a99a6c7a2183745418021b1c573b30e2e9a0ff50a87ff0a38c5b472577cfd76d4bae41e01e8bd001dd57bb227b14e0bc6a431c16f556e5c2a39a9433eaaa74ce5864a6fa7455888dc11867e716821c99be7854b6a19d8a07c1985a25374b472039ee351cf5f92e7b7706d04f53985d11ce0d3444a005eea3b8079b5bc319146b68478e87821dc0cad51a895eea5d9516410a4691896e72fecc627e4cb6c1815856568fbba046b5c52fe415857896389fb769b66de7d1635ab48a3c35f95361ab12635eabf88e2402

Results:

<Error>
   <Code>SignatureDoesNotMatch</Code>
   <Message>
      The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.
   </Message>
   <StringToSign>
      GOOG4-RSA-SHA256 20190918T143035Z 20190918/auto/storage/goog4_request 75304e5ca664b15a1fbbebca700d83f4fbe48fc7bfe8db70399ce7fbc3ba0b38
   </StringToSign>
   <CanonicalRequest>
      GET /data%2Fi4rNhrOGJgNGlzELwesBcVrSfnG2%2Fbooks%2Fqle8FcNGSNGXQGd1EElspA%2Fresources%2FQC5Qdp5gQgeCMw7AQ18nmw.m4a X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=api-service%40bookcreator-dev.iam.gserviceaccount.com%2F20190918%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190918T143035Z&X-Goog-Expires=12565&X-Goog-SignedHeaders=host host:storage.dev.bookcreator.com host UNSIGNED-PAYLOAD
   </CanonicalRequest>
</Error>

Without cname downloads the file: https://storage.googleapis.com/bookcreator-dev.appspot.com/data%2Fi4rNhrOGJgNGlzELwesBcVrSfnG2%2Fbooks%2Fqle8FcNGSNGXQGd1EElspA%2Fresources%2FQC5Qdp5gQgeCMw7AQ18nmw.m4a?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=api-service%40bookcreator-dev.iam.gserviceaccount.com%2F20190918%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20190918T143301Z&X-Goog-Expires=12419&X-Goog-SignedHeaders=host&X-Goog-Signature=636007c44a015244a9d30379dc326a82895c9b3bc0ee0ecd5317d36b4396937198bdd1d9c1f03690e2d26bf7e0ef351116b992e5cbdef25b74d509f0429dda8e7c516f8eb30d1f01656ae090bce206312af436af6df613ca6ae732fadd8da45172ed8cb424e3a96f4c472ec604bf802a86c4b6df1756d401d97ea25e5e4b1ddc1e96c650d3bb8fafd706d9228d282bb73de30025463ed0a03ad268ba06affa2234ea1ca0405e41a8a9006a8fb86719b326cc01696cc5cc0fa0836bac2016157274312f2dc4023f34e8b8d94dfc798910cfbad93591210c682bf45e27fffa0c101bc62a0c3cb5a53d15ca5a509f244b6e11bc30cfe7a667c4240c300d04022d0f

storage external help wanted needs more info p2 bug

Most helpful comment

@rhodgkins I've spoken to the product team and verified that slashes indeed should not be encoded when generating a signature. I'll implement a fix and update this thread when the fix is released. Thank you for your patience, again, and for your help in reporting the issue.

All 14 comments

Regarding the v2 issue, I've found that removing the encodeURIComponent of the object name from here allows it now to work regardless of the user-agent header...

This seems to be more of a backend issue now, as this was previously working without changing existing code!

@rhodgkins Thank you for the bug report and the detailed reproductions!

The v2 issue does seem like an issue with the service - for this the best course is to file an issue at the issue tracker to get a direct response.

For v4, it seems like the Node.js implementation did not use the cname provided in constructing the canonical request:
https://github.com/googleapis/nodejs-storage/blob/master/src/file.ts#L2590
I'll send a PR to fix this issue.

Thanks for your patience!

@jkwlui - thanks for getting back to me.

Regarding the v2 issue, I've already raised a support case in GCP (#20688201) and they came back with the following:

As per Engineering, a security issue came to light recently, and we can not safely support the form of URL you are currently signing (with URL-encoded slashes). You can work around this by not url-escaping the slash between bucket and object in your URL (use "/" instead of "%2F"). Cname-based cases are expected to be restored to functional in coming weeks.

Engineering has advised that URL-encoding slashes is discouraged, and will never be supported again for signed URLs to the default host of "storage.googleapis.com".

(They also raised an issue in the public tracker).

If it helps, I removed the encodeURIComponent from this line:
https://github.com/googleapis/nodejs-storage/blob/ace3b5e3819c7894159dd84f17aac0f21396e407/src/file.ts#L2481

And everything started to work as before (with cname based URLs at least).

Not sure if this will also apply to v4...

@rhodgkins I've tried to reproduce what you're experiencing with the v2 signed url with various user-agent headers, but I haven't gotten 403 back.

Given that Engineering has said that URL-encoded slashes is expected to be restored, can you see if you're still experiencing the issue? This is so that if we go ahead and make a fix to remove encodeURIComponent, we want to make sure it's addressing the issue that you raised.

Thank you for your patience!

@jkwlui sorry about the delayed response...!

I just wanted to check:

Engineering has said that URL-encoded slashes is expected to be restored

I was under the impression from the comment, that URL-encoded slashes will never be supported again?

I generated a new signed URL (with v3.3.1 of this package), and trying it in Chrome results in the 403 signature mismatch. I then copied the cURL form of the request from the network tab:

curl 'https://storage.dev.bookcreator.com/data%2Fi4rNhrOGJgNGlzELwesBcVrSfnG2%2Fbooks%2Fqle8FcNGSNGXQGd1EElspA%2Fresources%2FQC5Qdp5gQgeCMw7AQ18nmw.m4a?GoogleAccessId=api-service%40bookcreator-dev.iam.gserviceaccount.com&Expires=1570795200&Signature=WFTKpU%2BOUo%2FqgAckS2vvPkohfbbuX4l4BKZ7s%2BfhJDPBc5gte7UHox8sWWX5%2FDEub%2FgNAkf5QV3l%2FHSgXu8mPwMB%2B6vKx17v9oogug39ReF7oT%2BBcm3Tmrlz92f6%2F3DwnSIf2OthJzEZY31PISFUdnyHiy%2F9Uak52JVk7Ugcowa%2B3B1O%2BC%2BZvwLvwElwXMRuAicQvIvVKopnNolgBARZ6gN%2Fiswe5Ukax7QYPzP5VRglpOUjnTHWYYJbIGNGNY4l7g1R3ZIMpBU45SxJgufUevygvh6nJoTPQDHXWMovUn1ri0bxiWV%2FHNNFEtjbvPpTmwjhUqNzy35n4QonJCeltw%3D%3D' -H 'authority: storage.dev.bookcreator.com' -H 'upgrade-insecure-requests: 1' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36' -H 'sec-fetch-mode: navigate' -H 'accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3' -H 'sec-fetch-site: none' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: en-GB,en-US;q=0.9,en;q=0.8' -H 'cookie: _ga=GA1.2.707883014.1570783193; _gid=GA1.2.895096839.1570783193; bc_auth=eyJhbGciOiJSUzI1NiIsImtpZCI6ImQ2YzM5Mzc4YWVmYzA2YzQyYTJlODI1OTA0ZWNlZDMwODg2YTk5MjIiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiUXdlcnR5MTIzIiwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2Jvb2stY3JlYXRvciIsImF1ZCI6ImJvb2stY3JlYXRvciIsImF1dGhfdGltZSI6MTU3MDc4MzU4OCwidXNlcl9pZCI6IlgzQ01yQ1VjVDladnFId2xtVnhlS2RHY09SSjMiLCJzdWIiOiJYM0NNckNVY1Q5WnZxSHdsbVZ4ZUtkR2NPUkozIiwiaWF0IjoxNTcwNzgzNTkxLCJleHAiOjE1NzA3ODcxOTEsImVtYWlsIjoiYmlsYm9AYm9va2NyZWF0b3IuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImJpbGJvQGJvb2tjcmVhdG9yLmNvbSJdfSwic2lnbl9pbl9wcm92aWRlciI6InBhc3N3b3JkIn19.nYOYzat7bBxgpPJhLBYevABFX5n1sFVSW0rznTZ0lk1qcQ9EKZoTv66apJGSUdzJ0gaqRkYYuiYC8naP-Gq2IM1QbNZv9W_4YcKcSJEiMhFY2F132MbtM_RQPZqD24rQ24CGDnxGafWYXUYpBBBOZivAkzn-r33O4SNqI8B_GN_N3t8lY3j4nFcwJmnGfPbDGMGYDseEOnXYneoLxEdEnXid0lE18rRX9z1e1p8ZY8m1hbD_tFJ3iqxbYNNsIwv1r3phuET86GrMqxN8tK17TDqlxkFEaap3-vdwIN0OmRg9nM4OfxDQoySPHtQtPR7SCfiyGD0G15cMQp6nsY9u_Q' --compressed -vL

(I added the -vL flags myself).

Let me know if you need any more information!

What was the name of the file?

I wanted to double check, since in the response you got from engineering, it's mentioned that "You can work around this by not url-escaping the slash between bucket and object in your URL". We've only url-encoded the object name, not the slash between bucket and object:

https://github.com/googleapis/nodejs-storage/blob/ace3b5e3819c7894159dd84f17aac0f21396e407/src/file.ts#L2482

I've tried constructing a (v2) signed URLs with and without the encodeURIComponent line removed, and both URLs seem to have worked for me:

http://mydomain/test%2Fmultiple%2Fnested%2Ffolders%2Ftesting.png?GoogleAccessId=jonathanlui-private%40appspot.gserviceaccount.com&Expires=1571270899&Signature=cVXTpqKMZG%2B8q07%2BSiPBjCs07zFgg6KEvGg6HN3X1RieTDKTr1J0L6Y8dvHaS7BWE5xhERihEz70ApNk%2BMOtrOdv2iYTjaX6p7DwwzB7uuXtwmaMguRO1pEPz5syrkv7Uk34n7BHQqminvqtsLWqsvPFgLO7Cr%2FvOeNLs%2BaXJ1vN%2BlpXw2fjIB0exzTwArrfbQS%2F7V%2BR%2FsOkJcGHjz%2FY0nxQq6ohFBZAxjj74gmmHeQ2YxMDHNSc97y2JKdQ7r3ed3%2BYz%2FmZYj9eFEgOgqbOyDmWTD36HXHrqj87r5dVVso%2BAD1c0OwimG4JHMlyB7B5vpy2k1tVNSwsRXvtzbC%2FYg%3D%3D

http://mydomain/test/multiple/nested/folders/testing.png?GoogleAccessId=jonathanlui-private%40appspot.gserviceaccount.com&Expires=1571271063&Signature=Dp4q%2BNmBtDNfgxg2Xl86wnG6ja4gkBUVUYIw%2BeYyFcfYUVedOfc3XAiacNVukbXCTLcwGWjruVuxHs2cJrLimz09Jbp%2Beh9kkm5mg2mT82VDhBemTXx4c5RKabLXGUC1PbcLXfMvEQTPtg6abizcIKwxywdDOgDHIkUIBroVKRvzU0lII6BOa2Bmk8N%2BDHkHPJaEYl2CnN9Xk1vvJRofaFCe1Kg1webv05jt9DNEUh12%2FNofrKmH9%2B56PrDwPjCSlqSMRPjxk1Ct5LPJ9ItWP0xnT9%2Bk2RVFQ2xQ4BT28d0gvuBu2a2YdeLSKBlzCzeX3s%2FO%2BB4w9o%2BFVmDJ84qEKA%3D%3D

These are equivalent, except in the later one the slashes in the object name is not encoded (thus generating a different signature).

What was the name of the file?

/data/i4rNhrOGJgNGlzELwesBcVrSfnG2/books/qle8FcNGSNGXQGd1EElspA/resources/QC5Qdp5gQgeCMw7AQ18nmw.m4a

Regarding the second reply, I'm not sure how I can help there. All I've found is that viewing a signed URL with no URL-encoding slashes in the object name component works in the browser, but the same signed URL with encoded slashes does not work in the browser, resulting in a SignatureDoesNotMatch error.
(Note that a plain cURL with both URLs works).

Just to verify, the error occurs only when

  • v2 signature is used
  • cname is used
  • the request is made using the browser, or using equivalent headers via curl
    is that correct?

We released 3.3.1 to address the v4 bug, did it fix your issue?

Thank you for your continued patience!

Just to verify, the error occurs only when...

Yep to 2 of these, except for the 1st one (v2 signature is used)!
The v4 bug fixed in that release you mentioned was to do with the missing cname from the calculated signature, so it hasn't fixed this bug. I hadn't actually tried it since that version came out (as we're using v2).

Example v4 URLs with same issue:

With URL-encoded name does not work (using the browser, or using equivalent headers via curl):
https://storage.dev.bookcreator.com/data%2Fi4rNhrOGJgNGlzELwesBcVrSfnG2%2Fbooks%2Fqle8FcNGSNGXQGd1EElspA%2Fresources%2FQC5Qdp5gQgeCMw7AQ18nmw.m4a?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=api-service%40bookcreator-dev.iam.gserviceaccount.com%2F20191021%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20191021T133128Z&X-Goog-Expires=518361&X-Goog-SignedHeaders=host&X-Goog-Signature=190aaebc8b6c38b9ceaf7bc69fb01c3d2038185ddc1cd8af027b06e308ef83442223f0ccfab63bd9d2595f421af380b27d191355c1d53aa640b6649cd7585064cfc836406081edf3b849c137cdd8b76ec0c1c064daf17dee5ff8566d14c516172d96ceb741556ebb0520feecec4640b068a96b27e67de60d42d76a2289846851fff2dc44673249da8d43a5853cf28b1553b9165f0dcb3e38f4f367434e5f6ba472bc6070ba8f9d08031b41b88fcfe6c1f0dfe3a7294afc7016f86c6477bad750d6da655c2e643fd8ae4c4c99f18e07d7f7179348d6aac79b49f1f981cd0a7fc819fc93768d8b4cd6d309d70288ff4224ca51d6cf65552d249452f45a6da1851d

Without URL-encoded name - works:
https://storage.dev.bookcreator.com/data/i4rNhrOGJgNGlzELwesBcVrSfnG2/books/qle8FcNGSNGXQGd1EElspA/resources/QC5Qdp5gQgeCMw7AQ18nmw.m4a?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=api-service%40bookcreator-dev.iam.gserviceaccount.com%2F20191021%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20191021T133209Z&X-Goog-Expires=518320&X-Goog-SignedHeaders=host&X-Goog-Signature=26e47337e81df0536474bb9edf8dd536e5d2b665d566f05ed4acd7dd0b5c016e7c4e2543634b06603878c6df807bbf8ace7c9214966dc6fd8dea60ad6a018ae3ad6f770299a1e80ffa6e713c92e471b381635a600eac554281acadb96021bd5e6f8e244e2b251bc4d5aa5068d64bb106cda1ced4289abf288bb006b7cf0edcceb35933b9ab9df4e002826da67590ee4c586ebce64bfbdb301f86dc284ca489f006d84b115cd30790c7251fefac7172f190958522fecb2d767d286927b224cee58323e24ecf51c7ac6e8cacd292db2b7a4633748de960c7bd684405aba7dc4f69981e9430c75ce0cb6597459589ce905426e71f9e8f1332cffe86853e04b07df5

@rhodgkins I've spoken to the product team and verified that slashes indeed should not be encoded when generating a signature. I'll implement a fix and update this thread when the fix is released. Thank you for your patience, again, and for your help in reporting the issue.

This should be resolved in @google-cloud/[email protected] now. Please let us know if you're still seeing issues with URL signing. Again, thank you for your patience and help with fixing this issue. @rhodgkins

@jkwlui yep this has fixed it thanks - though it appears to have exposed another issue. I shall create an issue with more details. Thanks again for this fix!

@jkwlui I was searching before raising the issue and found #682.

It seems that a config simply changing v2 and v4 in a config doesn't work when responseDisposition is provided:

Works:

{
      version: 'v2',
      action: 'read',
      expires: 1573210000000,
      responseDisposition: 'inline; filename="QC5Qdp5gQgeCMw7AQ18nmw.m4a"'
}

Doesn't work:

{
      version: 'v4',
      action: 'read',
      expires: 1573210000000,
      responseDisposition: 'inline; filename="QC5Qdp5gQgeCMw7AQ18nmw.m4a"'
}

I had a brief look in the docs, and couldn't see anything mentioning the lack of support!

Would it be worth ignoring responseDisposition for v4 instead of it completely failing? Just my 2 pence anyway! Also is there any idea if this will ever be supported?

Was this page helpful?
0 / 5 - 0 ratings