Advanced

Signing a request

I put the secret in the body and trusted it. A body can be forged. Signing proves the sender: you hash the request with a secret only you and the server know.

What you will see

403

About 1 min of reading

Try It

Sign a request and see the server reject a forged one.

1

Send a valid signature. The server recomputes and accepts.

curl -s -H 'X-Signature: ' -H 'X-Timestamp: 1767225600' -d '{"a":1}' https://api.example/private

Before you run it, say out loud what it will print. Then open the answer.

What it prints
{"ok": true}
2

Tamper the body but keep the old signature. The recomputed hash differs, so it is rejected.

curl -s -o /dev/null -w "%{http_code}" -H 'X-Signature: ' -d '{"a":999}' https://api.example/private

Before you run it, say out loud what it will print. Then open the answer.

What it prints
403
The signature covers the exact bytes signed. Change any byte and the signature no longer matches. That is the proof working: a forged or edited request fails, which a plain secret in the body would not catch.

Data Flow

You hash the request bytes with a shared secret to make a signature; the server recomputes it and rejects a mismatch, and a timestamp window stops replays.
1 Hash request with secret
↓
2 Server recomputes
↓
3 Mismatch means forged
↓
4 Timestamp window stops replay
Why this works
Prove, do not reveal

Sending a secret proves nothing, it hands it over. Signing proves you know the secret without sending it: you hash the request with it. The server can verify the sender and reject anything forged, without the secret ever traveling.

Close the replay window

A signature is only as fresh as the request it signs. Include a timestamp and reject anything too old, so a captured signed request cannot be replayed forever. The window trades clock skew for security.

Examples

A token is not proof

Authorization: Bearer <secret>

A Bearer token is a copyable password. If it leaks, anyone can call. A signature proves the sender without sending the secret itself.

Sign the bytes

sig = hmac(secret, timestamp + body, 'sha256')
headers['X-Signature'] = sig
headers['X-Timestamp'] = ts

Hash the payload with your secret. The server recomputes it. A wrong signature means the request was not from you.

Replay window

if now - ts > 300: reject()

A valid signature can be replayed. Reject any timestamp older than a few minutes, so a stolen signed request stops working after a short window.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Safe to retry
Previous Lesson
Safe to retry
Next Lesson
The browser gate
The browser gate →