0

Currently, I download the image from my cloud storage bucket, encode it in base64 and then pass it in data uri in the src attribute of an img tag.

Is there any other way to do this ?

router.post('/images', function(req, res, next) {
    var image = bucket.file(req.body.image);
    image.download(function(err, contents) {
        if (err) {
            console.log(err);
        } else {
            var resultImage = base64_encode(contents);
            var index = req.body.index;
            var returnObject = {
                image: resultImage,
                index: index
            }
            res.send(returnObject);
        }
    });
});
TheProgrammer
  • 1,409
  • 4
  • 24
  • 53

1 Answers1

1

Is the image publicly readable? If so you can just use

<img src="http://<bucket>.storage.googleapis.com/<object>">

Otherwise you will need to generate a signed URL for a GET operation, which can be used in an image tag.

Joshua Royalty
  • 599
  • 3
  • 8
  • Sadly, those are 2 options I do not want to use for security reasons. Anyone could hit those urls to make my backend bend the knee. – TheProgrammer Jun 23 '17 at 19:55
  • Could you clarify your concerns about signed URLs? Generating a signed URL on the server side has a cost, but unless you need to generate thousands of them per second, it should be manageable for even a very small machine. – Brandon Yarbrough Jun 23 '17 at 20:51
  • @BrandonYarbrough It's about security. Anyone could get a signed URL legitimately from my website and then start hitting that URL with a bot or a botnet millions of times. Then get another signed URL when the current one expires and so on. That's why I currently download the images from the bucket on my server. No one gets access to my bucket except my server. The only problem is that loading 64bit encoded images is a bit slower, so I am wondering if there are other better solutions that are also secure. – TheProgrammer Jun 26 '17 at 10:51
  • @BrandonYarbrough It seems you did not ping me with @, I was not notified of your message and only saw it today. Please ping me for your answer, because I am extremely interested in it ^^ – TheProgrammer Jun 26 '17 at 10:54
  • @BrandonYarbrough I am mostly worried about a hacker making my cloud storage download bill explode. – TheProgrammer Jun 26 '17 at 11:06
  • It is possible for a hacker to do this, though it is pretty unlikely. We've yet to have an abuse case like that. If you want 100% control over who can access the file the next best option is to create another handler in your app to serve the data and reference it in IMG tags in the main handler. This handler will do whatever authentication / quota management you want to have, then simply proxy the data (in binary, rather than base64). This should help the latency problems, as it will not get the base64 expansion (~1.4x IIRC?) and will also parallelize the downloads. – Joshua Royalty Jun 26 '17 at 16:33
  • Signed URLs include an expiration time. If you are worried, set the expiration time to the very near future, say 5 seconds into the future. – Brandon Yarbrough Jun 26 '17 at 16:41
  • @BrandonYarbrough Wouldn't that mean the hacker would just have to reload the page every 5 seconds with his bot to keep getting a new url ? The problem is that signed URLs do not provide adequate protection :/ If I reduce the time, the hacker just needs to ask his bot to scrape a new url more often. Also, you forgot to ping me again ? At least, I was not notified of your comment :/ – TheProgrammer Jun 27 '17 at 12:06
  • @royalpeasantry That's exactly what I am doing atm. You can check this: https://stackoverflow.com/questions/44654810/why-is-my-solution-so-slow-and-how-can-i-improve-performance-of-the-query – TheProgrammer Jun 27 '17 at 12:09
  • @royalpeasantry Except I wasn't able to transmit the binary data from the server onto the client in an tag, I must have done something wrong, that's why I ended up using base64 encoding. Could you please tell me how to transform my code to transmit the data appropriately in binary ? – TheProgrammer Jun 27 '17 at 12:10
  • @TheProgrammer, could you clarify your goals? It sounds like you want end users to be able to anonymously download GCS resources from your public website. However, you are also worried that an attacker might attempt to download an object repeatedly with a goal of running up your bill, and you're looking for a way to prevent that. Is that the case? – Brandon Yarbrough Jun 27 '17 at 22:04
  • @BrandonYarbrough Exactly. I already found a way, you can check it here: https://stackoverflow.com/questions/44654810/why-is-my-solution-so-slow-and-how-can-i-improve-performance-of-the-query But it's quite slow – TheProgrammer Jun 28 '17 at 10:40
  • @BrandonYarbrough Signed urls are not a 100% secure method. When will Google Storage have a secure method of reading images that does not imply downloading them on the server to server them on the client ? For example, in my API Manager, I can restrict my firebase browser key to only be used by my domain. When will I be able to do something something similar to restrict reads of my files ? Is there no API in GCP that could act as a middleman between storage and my app and stops all request not coming from my domain ? – TheProgrammer Jun 30 '17 at 12:47
  • @BrandonYarbrough In the mean time, I am testing signed urls and running into a strange bug: https://stackoverflow.com/questions/44846330/why-do-my-signed-urls-get-an-error-403 – TheProgrammer Jun 30 '17 at 12:47
  • @TheProgrammer What do you mean by "restrict my firebase browser key to only be used by my domain"? I don't see anything in the documentation about it. – Joshua Royalty Jun 30 '17 at 16:01
  • I'm guessing it keys off of the 'referer' header, which can easily be spoofed in your attack scenario. 'referer' header restrictions are only useful for well behaved users. – Joshua Royalty Jun 30 '17 at 16:11