I am, for the first time, implementing file uploads using S3 (in this case specifically user profile avatar images) using Flysystem. I'm currently at the point where I have created an S3 bucket, and a user can upload an image, which is then visible online in the bucket console.
I now need the ability to display those images when requested (i.e. viewing that user's profile). I assumed that the process for this would be to generate the URL (e.g https://s3.my-region.amazonaws.com/my-bucket/my-filename.jpeg) and use that as the src of an image tag however to do this, the file (or bucket) must be marked as public. This seemed reasonable to me because the files within are not really private. When updating the bucket to public status however you are presented with a message stating;
We highly recommend that you never grant any kind of public access to your S3 bucket.
Is there a different, or more secure, way to achieve direct image linking like this that a newcomer to AWS is not seeing?
The warning is there because many people unintentionally make information public. However, if you are happy for these particular files to be accessed by anyone on the Internet at any time, then you can certainly make the individual objects public or create an Amazon S3 bucket policy to make a particular path public.
The alternative method to granting access is to create an S3 Pre-Signed URL, which is a time-limited URL that grants access to a private object.
Your application would be responsible for verifying that the user should be given access to a particular object. It would then generate the URL, supplying a duration for the access. Your application can then insert the URL into the src field and the image would appear as normal. However, once the duration has passed, it will no longer be accessible.
This is typically used when providing access to private files -- similar to how DropBox gives access to a private file without making the file itself public.
I would recommend to put cloudfront at the front (no pun intended) of the static assets, this way it would serve it all over their datacenters and not just the region you uploaded it, and I think this would charge you less because it does not use bandwidth from your S3 bucket.
This way you give cloudfront permissions to your S3 bucket and there is no need to set files public in your bucket manually. Google how to set IAM user for cloudfront and S3 to get you set up.
Related
I have an image upload functionality on my website - I upload images directly to s3 bucket. What I do is I made that bucket public, I fetch images on my website and use public links of images from the s3 bucket to show them as html img tags.
Users of my website will take these images, possibly as links, and use them on their websites. I can see several problems here:
links of images are direct links to aws
the whole bucket is public
there will be a lot of unpredictable reads
Its a better idea to use cdn here (probably cloudfront). How to integrate cdn into this process though?
Can I upload images directly into cdn, without storing them in s3? Why would I need s3 if I have a cdn?
Any suggestions? Thanks a lot!
S3 is still required, since Cloudfront uses S3 as its origin. S3 remains the data store, but Cloudfront caches these objects at the edge.
Check out this walkthrough on adding a Cloudfront distribution to cache your static files.
This will allow you to use your own url for the static content, as well as restrict direct access to S3 via Origin Access Identity
As already answered, cloudfront needs an 'origin' and often types that origin is S3, so you can't use just cloudfront - the images need to exist somewhere.
One precaution you could do (not clear from your question if you are - you said the links are 'directy to aws'), is to serve all of these images under your own domain name - either directly from s3, or with cloudfront - doing that now will ensure that if you do ever need to switch where you store the images, you customers won't need to change their links and the process would be seamless for them.
I'm using the AWS S3 bucket in a very simple way.
There's a bucket, <somebucket1>
There's a folder, <somebucket1>/sitevideos
And video files in it, <somebucket1>/sitevideos/video.mp4
I use this bucket so playback using HTML5 video (<video></video>) is more optimised and doesn't lag compared to just calling the video from the same server of the website (which is ridiculous).
The video files are encrypted, but they are set to be read-only to Public.
Now, my worries are, because they are public, people can download them from the S3 bucket instead of playing them on the website.
The Question
Is there a way to play a video file in S3 bucket, on an HTML video from a remote website, but will refuse downloads of the file if they are accessed directly via the S3 path?
If there are tutorials for this, I'd appreciate it. If this is already on the S3 documentation, I apologise for the laziness, please show me the link. I also heard that you can set them the permission to private, but they can still play on a remote server (although I haven't made that work).
Cheers & many thanks
A Bucket Policy can be configured to Restrict Access to a Specific HTTP Referrer.
For example, if a web page includes an image on the page, then the HTTP request for that object will include a referer. (I presume this would work for a video, too.)
However, this is not very good security, since the HTTP request can be easily manipulated to include the referer (eg in a web scraper).
A more secure method would be to use a Pre-Signed URL. This is a specially-constructed URL that grants time-limited access to a private Amazon S3 object.
When rendering the web page, your app would determine whether the user is permitted to access the object. If so, it would construct the pre-signed URL using AWS credentials. The URL can then be included in the standard HTML tags (eg <img src='...'>). The user will be able to access the object until the expiry time. If they shared the URL with somebody else (eg in a Tweet), other people would also be able to access the object until the expiry time.
By the way, Amazon CloudFront can also serve video content using various video protocols. It also supports pre-signed URLs (and also signed cookies).
I have a (php) website where teachers upload recordings of their class, and the students can log in and then play back the recording.
I want to make these videos more secure. Currently, the videos are stored on my server, and anyone with the url can download them. So, (1) I want to store them somewhere that can't be downloaded just using a url. And second, I need to stop them from right-clicking and saving the video as it is being played.
I'm trying to work this out with s3 but not getting it...
Is this possible? Does it need to use a special player? Does streaming the video help (can any video be streamed)?
I appreciate the help, I've spent many hours researching this and just getting more confused as I go along!
There are a couple of options you may wish to use.
1. Amazon CloudFront RTMP Distribution
Amazon CloudFront is a Content Distribution Network that caches content closer to users worldwide, in over 60 locations. It also has the ability to service Real-Time Media Playback (RTMP) protocols. This means that your web page could present a media player (eg JW Player, Flowplayer, or Adobe Flash) and CloudFront can serve the content.
See: Working with RTMP Distributions
CloudFront Distributions can also service private content. Your application can generate a URL that provides content for a limited period of time. The content is served via a media protocol, so the entire file cannot be easily downloaded.
See: Serving Private Content through CloudFront
2. Amazon S3 Pre-Signed URLs
By default, all objects in Amazon S3 are private. You can then add permissions so that people can access your objects. This can be done via:
Access Control List permissions on individual objects
A Bucket Policy (as per yours above)
IAM Users and Groups
A Pre-Signed URL
A Pre-Signed URL can be used to grant access to S3 objects as a way of "overriding" access controls. A normally private object can be accessed via a URL by appending an expiry time and signature. This is a great way to serve private content without requiring a web server.
Similar to the above example with CloudFront, your application can generate a URL that provides access to S3 content for a limited time period. Once the period expires, the Pre-Signed URL will no longer function. However, during the active period, people would be able to download the entire file, so this might not be advisable for video content you wish to protect.
I am creating a file sharing kind of web site (something like wetransfer). I was thinking of using S3 for storage and I want to use different hosting solution instead of EC2 so my web server will be in a different host outside amazon cloud. In order to reduce bandwidth consumption I will need to someway let clients to download and upload files directly from the client (browser).
I was looking at S3 documentation which explained how to directly upload file to S3 from browser client. It looks like we are pretty much exposing all details of my s3 credentials where some can easily look into details and abuse.
Is there any way I can avoid this by something doing something like allow users to upload/download files with a temporary credentials?
Would an IAM User Role work? You should be able to create a user (which will have it's own UUID), give it readonly access to your S3 repository, and pass that user's credentials into your request policy, as well as content and key rules.
If you want to grant all users read/write access, you can, though allowing those users access to specific files only, will be a bit more of a hassle.
I was looking at S3 documentation which explained how to directly upload file to S3 from browser client. It looks like we are pretty much exposing all details of my s3 credentials where some can easily look into details and abuse.
No, you're not. When used properly, the POST-based upload interface documented on that page only gives the user a limited-time authorization to upload one file matching various criteria (e.g, its name, size, MIME type, etc). It's quite safe to use.
Keep in mind that your S3 access key is not sensitive information. Exposing it to users is perfectly fine, and is in fact required for many common operations! Only the secret key needs to be kept private.
I am developing a web app where video files are stored on Amazon S3 and using CloudFront is an optional feature which can be turned on and off at any time.
I have a bunch of video files set with private ACLs, and I use signed URLs to access them. This works great.
However, I want to create a CloudFront RTMP distribution on that bucket, but it would be difficult to programmatically update every single (Could be well over 300) object's ACL each time (And would take a long time for all the requests to happen since you can't do it by batch, right?).
Is there a way to either:
Set ACLs in bulk, in one call?
Set a bucket access policy so that CloudFront can read any private files in the bucket?
I have attempted creating an Origin Access Identity, and then adding this to the bucket's Access Control Policy but this doesn't appear to work.
And finally do I still need to sign the URLs when I send them to the video player?
This does all need to be done programatically in PHP so using CloudBerry and such won't be helpful to me unfortunately.
This is a useful guide to get started, it tells how to set up the private distribution:
http://www.bucketexplorer.com/documentation/cloudfront--how-to-create-private-streaming-distribution.html
You can set the ACLs via the AWS API looping through your videos in a series (I don't think this can be done in bulk, even BucketExplorer does this in a queue). You only need to set the ACLs on each file once. You need to make sure you grant access to the Canonical User you have in your Origin Access Identity for the distribution. This way the distribution can access the protected file from the S3 origin. You then need to set up a key-pair and a trusted signer.
You do need to sign the URLs every time someone accesses the video. There are a number of scripts available. This is a useful guide for Ruby, but you could quite easily rewrite the code in PHP:
http://r2d6.com/posts/1301220789-setting-up-private-streaming-with-cloudfront-and-ruby-on-rails