AWS S3 bucket • Uploading objects with presigned URLs

PUT mission failed – 403 Access Denied

My team experienced an issue when generating a presigned URL and using the PUT method to upload images to an Amazon S3 bucket.

I haven’t experienced any issues with presigned URLs before. However, since developers have reported problems, I am trying to rule out any issues in the resource settings for which I am responsible.

What is a presigned URL?

As stated in the AWS documentation, you can use presigned URLs to grant time-limited access to objects in Amazon S3 without updating your bucket policy. That’s the best definition. A URL inherits permissions from the account that created it. It looks something like this:

https://myuniquebucket-development.s3.amazonaws.com/ubuntutestfile?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAUK4ETDN2T5UTZUYS%2F20260220%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20260220T133611Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Security-Token=IQoJb3YnZ2luX2VpEM3%2F%2F%2F%2F%2F%2F%2F%2.......

While generating a presigned URL for the GET method is relatively straightforward — for example, by using the AWS CLI:

aws s3 presign s3://$BUCKET/test --expires-in 30

The situation becomes more complicated when we need to generate a presigned URL for the PUT method in order to allow someone to upload content to our bucket. The AWS documentation provides guidance on this process: Create a presigned URL for Amazon S3 using an AWS SDK.

The Amazon S3 User Guide explains it in detail in the section: Uploading objects with presigned URLs. Using that approach, I attempted to reproduce and verify the issue reported by the developers.

Presigned URL dla PUT

Generating URL

From the workstation that has permission to access the S3 bucket, I run the Python script with the following parameters:

python3 put-only-url.py $BUCKET ubuntutestfile --region "eu-central-1" --content-type "application/octet-stream"

The URL to use is in the output.

Using URL

According to the documentation: Uploading objects with presigned URLs. I tested the URL from the host, who had neither access to the bucket nor an AWS account.

curl -X PUT -T testfile -H "Content-Type: application/octet-stream" "https://myuniquebucket-development.s3.amazonaws.com/ubuntutestfile?X-Amz-Algorithm=AWS4-HMAC-SHA256......"

It turns out that Access Denied is indeed the case. The GET method works without any problems. I checked the documentation again:

I had previously assigned the URL to a variable and tried it, but it made no difference. 403.

URL="https://myuniquebucket-development.s3.amazonaws.com/ubuntutestfile?X-Amz-Algorithm=AWS4-HMAC-SHA256......"

Solution

In short: The problem lies with the quotation marks in the URL. The solution is to omit them.

URL=https://myuniquebucket-development.s3.amazonaws.com/ubuntutestfile?X-Amz-Algorithm=AWS4-HMAC-SHA256......

curl -X PUT -T testfile -H "Content-Type: application/octet-stream" $URL

Test

On the machine with access to the bucket, I display the updated file contents on the stdout

aws s3 cp s3://$BUCKET/ubuntutestfile -
text from my ubuntu

P.S. I’ve sent some feedback to Amazon. We’ll see what happens with the document. In 2023, we could submit a pull request to the documentation repository…


In partnership with

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.