How to enable presigned S3 URL for different users?


I am trying to build a service where users are able to upload photos to an S3 database using presigned URLs given to them via API gateway. For each user, I was planning on submitting the photo information through the presigned URL and identifying the user who sent it as metadata via the ID found in the access token granted by AWS Cognito.

However, I am not sure how to secure it so that users can only upload photos as themselves and not as others. It seems to me that malicious users can simply modify the frontend code to change the user ID and submit photos as someone else, after receiving the presigned URL.

I'm wondering if it is possible to create a presigned URL with some sort of ID so that they can only submit content as themselves? Or is there a better way?

1 Answer

Presumably each user will be uploading a photo to S3 with a unique prefix and name and also (presumably) you are keeping track of which prefix/name is being used by which user because you need that later. Admittedly this can be done with object metadata but it would be unwise to let the users set their own metadata - you can't (as you point out) trust what the user is doing.

If a presigned URLs was obtained by a malicious user then they can definitely upload an image that will appear to belong to the original user. The best defense here is a short expiry time on the URL. If a malicious user completely "owns" the browser that the target user is using then there isn't much you can do about that.

If a malicious user was to modify the front-end code to appear as another user (presumably to obtain a presigned URL from API Gateway) then the call to API Gateway should fail - because the call should be authenticated and the user identity (that has been modified) will not match the token that is sent to API Gateway. The whole idea of the tokens issued by Cognito is the end-user can't create a fake one.

If your code depends on a field that is controlled by the user in order to generate the persigned URL then I'd suggest that you need to change that so that the user identity is determined from something that the user cannot modify.

profile pictureAWS
answered 2 years ago
  • Thanks for your suggestions. I agree that ideally I would be able to pull the photo identification via the access token user ID; however, I'm not sure how to do this. My code logic is currently:

    1. When user requests to upload photo, the user sends a PUT request to API gateway, which obtains a presigned URL and returns it to the user.
    2. Frontend uses this presigned URL and adds metadata, such as the user ID, and submits this to S3.

    My concern is that a malicious user can simply log in as one user, obtain a presigned URL, and change the code so that it submits as another user ID in the metadata. I don't see at which step I can change it so that the uploaded file is automatically attributed to the user that is authenticated; my thinking is that it would have to be on the API Gateway/server side to prevent tampering.

    Is there a better architecture instead of presigned URLs? Would it be better to instead have API Gateway use the presigned URL (instead of the frontend), and simply redirect the photo upload to this presigned URL via a Lambda? That way, API Gateway can handle metadata/user ID extraction. However, it would require routing large files from frontend to API Gateway to S3, which may hurt performance.

  • My thinking is that you should not allow the users to set metadata. In any particular security model, you can't trust things submitted to your application. Presumably you're doing some sort of post-processing on the upload to S3 (say, you're triggering a Lambda function). So, as you issue the presigned URL you also store (temporarily) the object prefix/name in a temporary place (I'd use DynamoDB) with the required metadata; then when the Lambda is triggered after upload it can retrieve the metadata and assign it to the object, deleting the DynamoDB record in the process. Extra work, yes.

  • (continued) but: More secure. As to uploading via API Gateway - you can definitely do this but be aware of the API Gateway and Lambda payload limits; the object can't be larger than 6MB due to the Lambda payload size. But if that works then you don't need presigned URLs at all.

    Given the apparent complexity of your application I'd strongly recommend reaching out to your local AWS Solutions Architect and having a conversation about this.

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions