New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
getSignedURL() in Google Cloud Function produces link that works for several days, then returns "SignatureDoesNotMatch" error #244
Comments
Thank you for reporting. Are you able to share the entire link that works, then eventually fails? It should probably be looked at by someone on the Storage team (cc @frankyn). |
IIUC, the issue here is that it expires when it shouldn't given the config at defined: index.js#L28 |
That looks right and matches the expires timestamp that we use in the constructed URL: |
I added a shorter Node script to generate blobToSign:
Shortened script: // Imports the Google Cloud client library
const Storage = require('@google-cloud/storage');
// Your Google Cloud Platform project ID
const projectId = 'project-id';
// Creates a client
const storage = new Storage({
projectId: projectId,
});
// The name for the new bucket
const bucketName = 'bucket';
bucket = storage.bucket(bucketName);
file = bucket.file("image.jpg");
CONFIG = {
action: 'read',
expires: '03-01-2500',
};
file.getSignedUrl(CONFIG, function(err, url) {
console.log(url);
}); I do notice no metadata (contentType) is being used when creating the blobToSign so this should provide the similar output due to different service accounts, bucket, and object names. @stephenplusplus does this look right? |
@colinjstief could you share the signed URL or provide the value inside |
@colinjstief one more request. Could you recreate this signedURL using my script above and the service account landlinc-stage@appspot.gserviceaccount.com ? I want to compare. |
Away from computer right now, will post this evening. |
Seems like I need a little more in that snippet related to credentials? Just running it via node on my local machine and it's throwing a "Cannot sign data without |
@stephenplusplus is the only way to define @colinjstief Instead of running the shorter script, could you re-upload the same object you did before with the same name? The goal would be to regenerate the same signed URL you provided earlier and compare the versions. |
@frankyn sorry for the confusion, but if I understood correctly, I generated a signed URL using the exact same cloud function (and environment) as before. Here it is: One thing to note, the url string prior to the query parameters is a little different because the logic of the process creates unique identifiers for that particular upload. |
Could you update the logic to use the same unique identifier as the prior signed URL? Technically, if the signedURL method provided all the same parameters it will generate the same signedURL. It doesn't use current time and also doesn't maintain state for the signed URL in the GCS backend. |
Here is the URL, created from a file with the exact directory as the original file. It appears to have a different signature: Another thing to note is that I do some cropping and compressing of the image, so technically it's not the same file each time. Would that change things? |
One difference between this version from the prior besides the different Signature is Given the different signature, I wonder if the private key for the service account was refreshed, maybe? Will try to reproduce the error and report back. |
That is strange. No, I just checked the change history and I’m 99% sure the library wasn’t updated. |
Hello,
These are two generated URLs for the exact same file (just re-uploaded and overwritten couple days later). Currently both links work (even though the signatures are different), but the first one will probably stop working a week from now approximately, the second one after two weeks. That estimation is based on previous observations. |
@frankyn I see that the first URL that @BorisBeast generated is now expired. Mine both seem to be working, but I expect that to change in the next few days. Will keep checking. |
Have the exact same problem. Everything is working fine (url is accessible by clients) but at some point (after few days, ~10 days) - it "expires" and returns: URLs created more recently are available and working as expected. |
@frankyn Both of the URLs created for this thread are now broken and throwing the same error. |
Hi all, Apologies for the delay: <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>
GET 1531165081 /coderfrank/51f7251dc8561126be000027_736.jpg
</StringToSign>
</Error> I'll follow-up internally with the Cloud Functions team. My current guess is the service account key used to provision your Cloud Function environment is deleted after a certain amount of time (5-7 days). The service account is used to generate a signedURL and when deleted renders the URL useless (SignatureDoesNotMatch). I appreciate everyone's patience on this issue. |
Not a problem, thanks for your work on this and I hope you got some time off last week for the holiday. |
@frankyn, I'm sorry to bother you, but I don't understand one thing. Is a new service account key generated each time I call getSignedURL()? If not, why is the signature different every time, but the links still work? |
Hi @frankyn, So would a possible workaround (for now) be to use an explicit service account in cloud functions (using an account key JSON file) instead of the default GCP authentication for the GCS SDK and the cloud function running it? |
re:@BorisBeast, at the moment I'm only guessing* until I get a better understanding of the Cloud Functions environment. When I do I'll have an answer to your question. re: @shaibt, If my guess is correct, then potentially yes including the service account in your deployed Cloud Function could be a workaround, but I would not recommend it as a best practice yet. |
Part follow-up: GCF team pointed me to documentation located at Docs
So the Google Managed key is rotated on a weekly basis and makes sense given the behavior everyone has encountered. Looking into a best practice next, for now as @shaibt mentioned, a user managed service account would be a workaround for this behavior. |
@shaibt @colinjstief Hey guys, since I was not really satisfied with the outcome of this, I kind of looked for a "clean" way to handle this (from my point of view), and here is what I came up with: I am using the firebase CLI to switch between my environment, with the Variables set this way are only relative to the current used environment, so it's good to switch between different configurations easily. This way, I also do not have any files stored anywhere on my disk, or on my version control, no need to upload a file to cloud function either. I then initialize my firebase admin SDK this way:
And it works like a charm ! :) This is the cleanest way I found to deal with this environment / private key storage safety concern. |
For details on why there's an issue: googleapis/nodejs-storage#244 (comment)
For details on why there's an issue: googleapis/nodejs-storage#244 (comment)
@schankam I love your solution, but I haven't been able to get the CLI to play nice! How did you actually get your service-account json file into the CLI? Copy pasting it into 'functions:config:set' generates too many weird artifacts, and escape characters, and then it cannot be read in the actual functions/index.js Thanks for your answer, this problem has been killing me! |
Below is my cloud function code about when an image is uploaded then a thumbnail is created , getSignedURL for thumbnail and save it into database.
|
Hey all, was @mcdonamp 's solution of using In my case I'm not able to get any access whatsoever using all the techniques outlined above (see details in my so post), and I cannot find any documentation for UPDATE: Turns out I was having the same problem as this issue, where simply adding |
I'm having the same issue, my Firebase Storage signed download URLs expire after about three days. I see that this discussion is closed but I don't see any answer marked as solving the problem? I have the
|
hi, did you solve it? i dont have 50 000 but it would be greate to regenerate a couple of dozens |
as i see the sollution is to explicit tell the service account. but that shouldnt be the problem, the cloud function use the default service account.. If that service account has the necessary permissions should work fine |
Is there a cost associated with repeatedly doing a |
The default service account keys get rotated every once in a while (~7 days) so a URL signed with a certain key might become invalid a few days later after the keys change. The solution is to use an explicit account where the key is controlled by the dev and doesn't change. |
I'm disappointed that we're being bitten by this issue near the end of 2020, more than two years after it was reported (and we're using v4 urls that expire after only one day: we were bitten by this inside of a day.) That this issue still exists makes it feel like the different Google teams are not working well with each other. I understand the reasons given by @asciimike and that's understandable for a small period of time, but not a year, and certainly not as many years as it has been. Assuming that Google wants projects to use Firebase in production, then this issue needs to be solved. Why is it closed? Suggestion in the meantime: Firebase documentation should be amended to avoid the |
I no longer work at Firebase, so I'll unsubscribe from the issue and leave it in the hands of @schmidt-sebastian and @paulb777 (or let them delegate) |
this is the reason ...
*Required*. This is the timestamp (represented as the number of seconds
since the Unix Epoch of 00:00:00 UTC on January 1, 1970) when the signature
expires. The server rejects any requests received after this timestamp, as
well as any requests received after the key used to generate the signed URL
is rotated. For security and for compatibility with the V4 signing process,
you should set Expires to correspond to at most 1 week (604800 seconds) in
the future.
https://cloud.google.com/storage/docs/access-control/signed-urls-v2
…On Fri, Oct 9, 2020 at 7:36 PM Mike McDonald ***@***.***> wrote:
I no longer work at Firebase, so I'll unsubscribe from the issue and leave
it in the hands of @schmidt-sebastian
<https://github.com/schmidt-sebastian> and @paulb777
<https://github.com/paulb777> (or let them delegate)
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#244 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AJ5N4PLUTXUNOEM7HOB7GATSJ6F6FANCNFSM4FF7V75Q>
.
|
This is a closed issue, so probably best to open a new issue and refer back to this one. |
Long-lived URL creation is currently not supported via |
@schmidt-sebastian In order to avoid the collateral damage discussed in the later parts of this thread, documentation improvements could be made. For example, guidance to avoid the serviceAccountId / IAM method of initializing firebase apps in Functions if you are using Firebase Storage. It is not at all clear that this issue exists, and the cost of discovering late that your links are all invalidated before their expiryDate can be high. The likelihood of discovering this late is also high: if you use short expiration dates (1 day) and you never run a test at exactly the unknown window of that 1 day inside the window when server keys are re-rolled, then you'll never hit this. Totally possible to get to production and then have customer-facing red-face issues. |
There is an obscure way of generating long-lived download URLs using this library. See https://stackoverflow.com/questions/42956250/get-download-url-from-file-uploaded-with-cloud-functions-for-firebase/43764656#43764656 We've had a few discussions about making this part of the SDK API surface, so developers can use it easily. We should try to revive that discussion, and it could be the long term solution for this problem. |
+cc @egilmorez for docs |
Super helpful answers but what should I do if I already have production with hundreds of broken links? Regenerate all of them with @hiranya911 method? Or any other ways as regeneration will be super difficult action in my case. (╯°□°)╯︵ ┻━┻ |
Apologies @mckrava but that's the only method at this time. |
We're squarely in this position now, and finding out about this honestly sort of angers me. We've been chasing our tails on this forever assuming our users were crazy. Never did we realize that ya know, our links given an expiry time still expire. |
Thanks for the extensive research done by all here! After considering all the options I decided that having the signed URLs refreshed regularly with a scheduled cloud function is the best solution for me. Pro: avoids the security and management issues with having a user-generated service account
|
Environment details
@google-cloud/storage
version: 1.6.0Steps to reproduce
Repo of relevant code:
https://github.com/colinjstief/getSignedUrl-example
Lots of this code is copied/pasted straight from official example (link here). Code in my repo runs as a Google Cloud Function and produces a link that works for a few days (maybe 7?), and then seems to expire with "SignatureDoesNotMatch" 403 response. Full message reads:
"The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method."
URL seems valid, including this query parameter: '...&Expires=16730323200&Signature=...'
Both the initial request and the later request (after apparent expiration) are performed the same ways: either through an axios.get() or simply placing in browser bar. Both work at first, then both fail later.
Same issue seems to be reported here googleapis/google-cloud-node#1976, here #144, and here firebase/functions-samples#360
The text was updated successfully, but these errors were encountered: