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...