Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Viewing private images uploaded on AWS S3 - Creating signature from Secret Access Key

I upload some images on a Rails app through Paperclip gem which I want only the admins on the backend to be able to view. As a result, I have set them as private.

Then I searched around for a solution on how just admin's with specific links can be viewing the files. This is what I found.

I went on to try this but I am struggling on creating the needed signature. The formula is given on the above link and is:

Signature = URL-Encode( Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) ) );

StringToSign = HTTP-VERB + "\n" +
    Content-MD5 + "\n" +
    Content-Type + "\n" +
    Expires + "\n" +
    CanonicalizedAmzHeaders +
    CanonicalizedResource;  

I had no clue which library/module/gem I need to get this to work on Ruby on Rails. By searching around I found about the aws-s3 gem. I read their wiki and saw this "Accessing private objects from a browser".

So I installed them gem, went on and open my rails console and tried to test them out. I was able to make a connection with AWS S3 but then I cannot do anything as I get errors like "uninitialized constant" and "NameError".

Any tip/guidance to the right direction is appreciated.

Last note, what I try to do is generate links that the admins will be able to use for viewing the images on their browser and not downloading them. From what I read this is what it does. But is it for sure or just downloads them to the admin's computer?

like image 426
Stefanos.Ioannou Avatar asked Jun 14 '16 10:06

Stefanos.Ioannou


3 Answers

Since you are using Paperclip, you can just use its expiring_url feature. So you could have a #show or #download controller action that just redirects to the temporary URL. There is no need to implement it all yourself.

I will add that although the docs above recommend 10 minutes for the expiration time, I've found that if your server's clock skews one way and S3 skews another, the URL can expire before it exists. So maybe 20 or 30 minutes is safer.

Obviously if you add a controller action like that you should make sure only admins are allowed to use it.

like image 57
Paul A Jungwirth Avatar answered Oct 20 '22 01:10

Paul A Jungwirth


Firstly there is no difference between being able to view an image in the browser or being able to download an image. Either way the user's browser gets the bytes that make up the image and can do whatever it wants with them.

Amazon provide the aws-sdk gem as the officially supported way to access their services, including s3. To generate an s3 presigned url you would do something like

s3 = Aws::S3::Resource.new(region: "us-east-1")
s3.bucket("bucket-name").object("key/for/object").presigned_url("get", expires_in: 3600)

Which returns a link valid for 1 hour (this assumes you have instance provided credentials or have environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY with your credentials. If not you need to specify then when creating the resource object. )

like image 20
Frederick Cheung Avatar answered Oct 20 '22 00:10

Frederick Cheung


Defines a downloadable? method that can be used to implement user access rights to the track. For simplicity it just allows all logged in users to access the track, however you can replace this with whatever logic your app requires.

def downloadable?(user)
    user != :guest
end

and modify your attachemnet model like this

has_attached_file :mp3,
                  :url => ':s3_domain_url',
                  :path => 'assets/:class/:id/:style.:extension',
                  :storage => :s3,
                  :s3_credentials => File.join(Rails.root, 'config', 's3.yml'),
                  :s3_permissions => 'authenticated-read',
                  :s3_protocol => 'http'

have a look at this

like image 1
Subhash Chandra Avatar answered Oct 19 '22 23:10

Subhash Chandra