Only make S3 files accessible through ajax - amazon-web-services

I want to use S3 to store user uploaded excel files - obviously I only want that S3 file to be accessible by that user.
Right now my application accomplishes this by checking if the user is correct, then hitting the URL https://s3.amazonaws.com/datasets.mysite.com/1243 via AJAX. I can use CORS to allow this AJAX only from https://www.mysite.com.
However if you just type https://s3.amazonaws.com/datasets.mysite.com/1243 into the browser, you can get any file :P
How do I stop S3 from serving files directly, and only enable it to be served via ajax (where I already control access with CORS)?

It is not about AJAX or not, it is about permissions and authorization.
First, your buckets should be private unlike their current state which is world visible.
Then in order for your users to connect, you create a temporary download link which in AWS world called S3 Pre-signed Request.
You generate them in your back-end, here is a java sample
Enjoy,
R

Related

Could Amazon S3 Presigned URL be generated so a user can read a file but not download it?

We have a requirement to provide a user with temporary access to video files stored in Amazon S3. Users with a presigned URL should be able to play a video but not download it.
Is there any option to generate a presigned URL so users can play a video in a browser without the "Download" option?
There is no difference between 'reading' and 'downloading' the response to a URL.
You could choose to stream content, which means that the browser actively requests segments of the file rather than simply receiving the whole file. This avoids the ability to 'download' a file, but smart people can still obtain the entire contents by requesting all the segments. Not even Netflix can prevent this from happening.

Custom authentication with JWT for download from S3

I have a setup on AWS where I'm running a backend on ElasticBeanstalk/EC2, which stores some files in S3. What I'd like to do, is when an authenticated (by my backend) user wants to download a file, they can do so directly from S3, instead of going through the backend itself.
To that end, I'd like S3 to check a signed JWT before allowing a file download. For now, let's assume that any correctly signed JWT allows any file download, regardless of the JWT claims. To make this more difficult, I want the download links to be usable in HTML <img src='link/to/s3/'>, so sending the JWT in a header as you normally would isn't feasible.
Is this even possible with S3? How would I go about setting this up?
I'm not very familiar with S3, but doesn't it already have a concept of "signed URLs"?
Instead of issuing a JWT, your web app instead uses the S3 proprietary method to generate a URL which encodes the expiry time and other parameters, but the overall result is the same.

Is it possible to generate a single presigned URL which can allow MULTIPLE unique objects to be uploaded?

I know based on the AWS docs here
https://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObject.html
that its possible to generate a URL which can can used to
upload a specific object to your bucket
and that
You can use the presigned URL multiple times, up to the expiration date and time.
It is also possible to generate a URL (perhaps a base s3 presigned URL) which would allow multiple different unique documents to be uploaded based on a single URL?
For example, lets imagine a client application would like to upload multiple unique/distinct documents to s3 using some type of presigned URL. I dont necessarily want to force them to get a batch of presigned URLs since that would require much more on the part of the client (they would have to request batch of presigned URLs, rather than a single URL)
Here is the flow for a single document upload.
What is the simplest known solution for allowing a client to use some type of presigned url to upload multiple documents?
It is also possible to generate a URL (perhaps a base s3 presigned URL) which would allow multiple different unique documents to be uploaded based on a single URL?
A presigned URL is limited to a single single object key. You can't, for example, presign a key of foo and then use it to upload foo/bar (because that's a different key).
That means that, if you want to provide the client with a single pre-signed URL, the client code will have to combine the files itself. For example, you require the client to upload a ZIP file, then trigger a Lambda that unpacks the files in that ZIP.
Another approach is to use the AWS SDK from the client, and use the Assume Role operation to generate temporary access credentials that are restricted to uploading files with a specified prefix using an inline session policy.
A third approach is to hide the URL requests. You don't say what your client application does, but assuming that you let the user select some number of files, you could simply loop over those files and retrieve a URL for each one without ever letting your user know that's happening.
It is possible to upload multiple files with a single pre-signed URL and properly configured 'Starts-with' policy. Please, refer to the following AWS documentation: Browser-Based Uploads Using POST

Security concern in direct browser uploads to S3

The main security concern in direct js browser uploads to S3 is that users will store their S3 credentials on the client side.
To mitigate this risk, the S3 documentation recommends using a short lived keys generated by an intermediate server:
A file is selected for upload by the user in their web browser.
The user’s browser makes a request to your server, which produces a temporary signature with which to sign the upload request.
The temporary signed request is returned to the browser in JSON format.
The browser then uploads the file directly to Amazon S3 using the signed request supplied by your server.
The problem with this flow is that I don't see how it helps in the case of public uploads.
Suppose my upload page is publicly available. That means the server API endpoint that generates the short lived key needs to be public as well. A malicious user could then just find the address of the api endpoint and hit it everytime they want to upload something. The server has no way of knowing if the request came from a real user on the upload page or from any other place.
Yeah, I could check the domain on the request coming in to the api, and validate it, but domain can be easily spoofed (when the request is not coming from a browser client).
Is this whole thing even a concern ? The main risk is someone abusing my S3 account and uploading stuff to it. Are there other concerns that I need to know about ? Can this be mitigated somehow?
Suppose my upload page is publicly available. That means the server
API endpoint that generates the short lived key needs to be public as
well. A malicious user could then just find the address of the api
endpoint and hit it everytime they want to upload something. The
server has no way of knowing if the request came from a real user on
the upload page or from any other place.
If that concerns you, you would require your users to login to your website somehow, and serve the API endpoint behind the same server-side authentication service that handles your login process. Then only authenticated users would be able to upload files.
You might also want to look into S3 pre-signed URLs.

Is it good practise to give users ability to s3 direct upload using pre-signed URL?

I'm writing app that handles large files upload (eg. 10GB). I want to use direct upload to S3 (pre-signed URL) and give that possibility for my web users. My steps are:
I'm creating IAM user with only "PUT" permission
I'm creating upload policy on the server side (and putting there information about max file size, file content type and policy expiration time (eg. 3 hours)
Web user is uploading the file using html form with that policy and pre-signed URL.
I'm checking the file headers on a server side after succesfull upload.
Now, I'm wondering about downsides and security issues of this approach. There are any?
Thank you.