Presigned URLs are technically a security vuln

ErenayDev1 pts0 comments

Presigned URLs are technically a security vuln | Tigris Object Storage

Skip to main content

A presigned URL is a replay attack you did on purpose.

Replayable auth tokens are the textbook way to create vulnerable systems, but<br>Tigris ships them as a first-class feature with presigned URLs and so does every<br>other object storage system on the planet. However this isn’t an oversight<br>because presigned URLs turn a weakness into a feature.

Replay attacks are a real problem and the classic fix is miserable​

When you authenticate a request with Amazon’s SigV4 protocol for Tigris, your<br>client boils down the request to a canonical form: a SHA256 hash of the<br>request’s method, path, query parameters, signed headers and a SHA256 hash of<br>the payload. It runs the result of that through HMAC with a signing key derived<br>from your secret access key. Nothing secret ever crosses the wire. The server<br>derives the same key as the client, does the same canonical form transformation,<br>and compares the result.

Being able to make a valid signature proves that the request came from someone<br>holding the secret access key, but it proves nothing about when that request<br>was made. A signature that was made a year ago would still be valid today or any<br>other time you send it, so in theory an attacker could warehouse your signed<br>requests only to replay them en masse later. Imagine sitting on a pile of signed<br>“create EC2 instance” calls only to spam them all out at a later date. You would<br>be a twirling moustache villain able to spawn dozens of servers at a moment’s<br>notice.

Traditionally the fix is to bake a nonce (number used once) into the signature<br>(sorry to any British readers in the audience). This makes every signature<br>differ because that nonce differs.

However with great power comes great responsibility and making sure that<br>something used once is only used once is a surprisingly hard distributed systems<br>problem. You can’t verify that something is only used once locally. Say you<br>store them all for a 15 minute smear window at a low request rate like 10,000<br>Bq. That’s 9 million live nonces, and every frontend node needs to have a<br>consistent view of the whole set as it churns.

You have made your fast authentication check slow from having to ensure things<br>are only used once.

What you want instead is something that changes constantly without coordination<br>and invalidates those old signatures for free. For an added bonus you want this<br>to also be in the standard library of every programming language.

Sign the clock​

There’s exactly one value that changes constantly, (mostly) monotonically, and<br>is already actively coordinated across all elements of the stack: the clock.<br>Your OS already keeps time in sync with the public NTP pool (or a private NTP<br>pool if you are cool enough to have radioactive PCI cards laying around).<br>Without an accurate view of time you can’t make TLS connections, which means you<br>can’t make API calls to Tigris at all, so the auth layer gets to assume a<br>working clock exists.

SigV4 signs the current time into the request. If an attacker gets their greasy<br>hacker paws on a signature, they have about 15 minutes to use it before it<br>becomes a digital paperweight. If time is an input to the signature and the time<br>changes enough to invalidate the signature, the signature is null and void. Sure<br>in theory a sufficiently funded attacker could create a black hole in your<br>datacentre and disrupt temporal flow, but at that point the planet is probably<br>toast which makes the attack profile moot. Commit mass object storage fraud with<br>this one neat trick! The department of temporal investigations will have hated<br>it!

This makes your verification stay stateless. Everything gets checked against the<br>system clock the server already needs and you can give clients a 15 minute<br>signature smear window as a grace period for old or delayed clients (exponential<br>backoff is a good thing and Tigris will reward you for doing it).

Of course the real thing keeping the signatures safe on the wire is TLS (HTTPS).<br>If that is broken we have bigger problems and object storage fraud is the least<br>of our problems.

Time is the only nonce you need because both sides already agree on it anyways.

Some thorns have roses​

Presigned URLs take the replay tolerance that SigV4 spends all this effort<br>nerfing and then buffs it into the feature. The entire auth dance gets flattened<br>into URL parameters that any HTTP client can use, be it a browser, curl, Go’s<br>net/http, or something you made by bit-banging HTTP over a socket. Here’s a real<br>presigned URL I sundered into visibility:

https://xe-sophia-base.t3.tigrisfiles.io/moby-dick.txt

?X-Amz-Algorithm=AWS4-HMAC-SHA256

&X-Amz-Credential=tid_ubYBNEYAmTciLVwszw_QrUXDmtcyQisryryGfxgznDsCnOvNqh/20260714/auto/s3/aws4_request

&X-Amz-Date=20260714T043308Z

&X-Amz-Expires=3600

&X-Amz-SignedHeaders=host

&X-Amz-Signature=0dcaf4972911527a7582ff36ea457e9760a8efccb6655a178685aaa281637a36

Here are the parts (forgive the AI looking...

signature presigned request time urls tigris

Related Articles