Put request to S3 resulting in 403 (Forbidden)

0

I set up a S3 bucket {bucketName} and an IAM user, {iamUsername}. The user has the following policies to access the bucket:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Statement1",
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::{bucketName}",
                "arn:aws:s3:::{bucketName}/*"
            ]
        }
    ]
}

And the bucket has the bucket has the following bucket policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::012345678901:user/iamUsername"
            },
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::bucketName",
                "arn:aws:s3:::bucketName/*"
            ]
        }
    ]
}

Other than this, the bucket blocks all public access as recommended by AWS. I use the access key of the user to pre-sign the URL to upload a file in Flask:

s3 = boto3.client('s3', aws_access_key_id=AWS_ACCESS_KEY_ID,
                          aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
                          region_name=AWS_REGION)
presigned_url = s3.generate_presigned_url(
            'put_object',
            Params={
                'Bucket': S3_BUCKET,
                'Key': random_file_name,
                'ContentType': file_type
            },
            ExpiresIn=3600  # URL expires in 1 hour
        )

I use this signed url to upload the file in react:

const uploadFile = async (signedUrl) => {
    const uploadFormData = new FormData();
    uploadFormData.append('file', selectedFile);

    try {
        await fetch(signedUrl, {
          method: 'PUT',
          body: uploadFormData
        })
        .then(response => {
          console.log(response);
          if (!response.ok) {
            console.log(response);
            throw new Error(response);
          }
          return response.json();
        })

This is resulting in a 403, like below:

body: ReadableStream
bodyUsed: false
headers: Headers {}
ok: false
redirected: false
status: 403
statusText: "Forbidden"
type: "cors"
url: "https://{bucketName}.s3.amazonaws.com/{fileName}?AWSAccessKeyId={key}&Signature={signature}&content-type=image%2Fjpeg&Expires=1709497547"

CORS for the bucket have been set as below:

 [
        {
            "AllowedHeaders": [
                "*"
            ],
            "AllowedMethods": [
                "GET",
                "PUT",
                "DELETE",
                "HEAD",
                "POST"
            ],
            "AllowedOrigins": [
                "*"
            ],
            "ExposeHeaders": [],
            "MaxAgeSeconds": 3600
        }
    ]

Would appreciate help in figuring out what is wrong with the approach, thanks!

2 Answers
1
Accepted Answer

This was resolved by adding content type headers when creating the put request:

await fetch(signedUrl, {
          method: 'PUT',
          headers: { 
            'Content-Type': filetype 
          },
          body: selectedFile
        })

Credit to: https://stackoverflow.com/questions/78097844/s3-put-request-to-using-a-pre-signed-url-resulting-in-403-forbidden

Sid
answered 7 months ago
profile picture
EXPERT
reviewed 7 months ago
0

Hello,

Could you verify the CORS has been correctly setup and you can access using CURL ? Follow this article - https://repost.aws/knowledge-center/s3-configure-cors

AWS
EXPERT
answered 7 months ago
  • Hi Ranjan,

    The CORS configuration for my bucket is:

    [
        {
            "AllowedHeaders": [
                "*"
            ],
            "AllowedMethods": [
                "GET",
                "PUT",
                "DELETE",
                "HEAD",
                "POST"
            ],
            "AllowedOrigins": [
                "*"
            ],
            "ExposeHeaders": [],
            "MaxAgeSeconds": 3600
        }
    ]
    

    However, when I try to access it using:

    curl -i http://(my-bucket).s3.amazonaws.com/cors-test.html -H "Origin: https://www.example.com"
    

    I get 403 forbidden:

    HTTP/1.1 403 Forbidden
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Methods: GET, PUT, DELETE, HEAD, POST
    Access-Control-Max-Age: 3600
    Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
    x-amz-request-id: TF9YTYR36PBDQ71B
    x-amz-id-2: i/R8gtlFfsQGKyAnWWSfkE7UYxm+gcvjqvxdkTYINaZreSVx7qWI+UusYX3QHrxyddizgjGFqEE=
    Content-Type: application/xml
    Transfer-Encoding: chunked
    Date: Sun, 03 Mar 2024 23:01:52 GMT
    Server: AmazonS3
    

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