Gustaw Fit Blog

Your are unique lovely people. Read my blog for why.



In many posts – please scroll below Polish version to get to English version or vice-versa (not a rule!)
W wielu postach – proszę przewinąć w dół pod wersją polską, aby dotrzeć do wersji angielskiej lub odwrotnie (nie jest to reguła!)

So, someone finally told you that making your S3 buckets “Public” is a one-way ticket to a data breach and a very awkward conversation with the Chief Techdebt Officer. Welcome to the world of Pre-Signed URLs, the digital equivalent of giving someone a temporary key to your house that self-destructs after ten minutes. It’s a great system, assuming you actually know how to configure it.

A pre-signed URL is essentially a way to grant temporary access to a specific AWS S3 object using your own security credentials. You generate a link, hand it to a user, and they can download (or upload) a file without needing an AWS account. It’s elegant, it’s secure, and it’s the only thing standing between you and a “leaked database” headline.

If you’re using Python, you’re likely using boto3. Here is how you generate a link that lasts for one hour. Don’t set it to expire in a year. Please. Well, actually up to you :). I had to use Node :).

import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
const client = new S3Client({ region: "us-east-1" });
async function getUrl() {
const command = new GetObjectCommand({ Bucket: "my-grumpy-bucket", Key: "file.pdf" });
// Link expires in 60 seconds because I don't trust you.
const url = await getSignedUrl(client, command, { expiresIn: 60 });
console.log(url);
}

But … haha! This is where the fun starts.

You’ve generated the URL. It works when you paste it into your browser tab. But the second your frontend developer tries to fetch() it or show it in a fancy gallery, the console explodes with CORS errors.

When a browser tries to access a resource on a different domain (like your app trying to hit s3.amazonaws.com), it sends a “preflight” request. S3 will politely decline this request unless you’ve explicitly told it that your domain is a “friend”.

Example:

A cross-origin resource sharing (CORS) request was blocked because of invalid or missing response headers of the request or the associated preflight request.
To fix this issue, ensure the response to the CORS request and/or the associated preflight request are not missing headers and use valid header values.
Note that if an opaque response is sufficient, the request's mode can be set to no-cors to fetch the resource with CORS disabled; that way CORS headers are not required but the response content is inaccessible (opaque).

You gotta fix it. Well you don’t have to and just “play dumb”, but it rarely works in your favour. 😀

Go into your S3 Bucket permissions and find the CORS (Cross-Origin Resource Sharing) section. Stop putting * in the AllowedOrigins. Be a professional. Lol.

[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedOrigins": ["https://www.your-actual-app-domain.com"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
}
]

If you are doing PUT requests with signed URLs, you must include PUT in AllowedMethods and likely Content-Type in AllowedHeaders. If you forget this, your frontend dev will spend three days crying in a Slack channel. So did I. Because I was both the backend and frontend dev and wanted to spare myself the crying.

Now, let’s talk about why I’m currently looking at an AWS environment that looks like it was configured by a caffeinated squirrel. I got nothing against squirrels btw. Though they should NOT drink coffeeeeeee.

We’ve all inherited one: the Dave System. Dave was a “genius” who did as much as possible “manually” to push things over the line. Cut corners and added many comments with “Never change this!!!”. Used many technologies to look smart. In my case even used multiple programming languages for “added efficiency” as the confluence says. He clicked buttons, checked boxes, and hand-rolled “stuff” in the middle of the night. Then Dave left for a crypto startup in 2022. (not the case here, but with this packed CV, he likely ended up a staff engineer at AWS :P).

Now I am the post-Dave. The cleaner. Trying to make sense out of all of it and needing to tell people – “you don’t want to ask more questions”. Coffee keeps me sane though.

You can be Dave or you can be yourself.

As per usual – the choice is yours!


Leave a comment