Let's follow one request from the client's first thought to the server's final nod.
Hashbert the Hedgehog will annotate the important bits, because authentication is
easier to understand when someone points at the spiky details.
Step 1
Declare the range of URLs you control
Ahead of time, the client administrator registers the exact narrow HTTPS URL or folder
where verification hashes will be published. The service uses this declaration to map the
URL back to the client and must reject URLs outside it. Avoid areas where the public might
be able to inject text such as comment forms.
My verification hashes will be published at:
https://client.example/api/hashback?id=*
🦔 Hashbert says: “Choose a small patch of ground that you control. A single doorway is splendid, but handing over the whole house is a bit much, even for a hedgehog with a clipboard.”
Step 2
Write the authentication request JSON
When you want to make an authenticated request, write a fresh JSON claim. This package
of data will combine the version, destination host, current UTC time, a unique random
value, and the URL where you're going to publish the hash of this JSON later.
The Unus value prevents anyone from reusing your verification hash to impersonate
you. Everything else in the JSON is predictable, but this field is fresh, random and known
only to you and the server you’re talking to.
Cryptographers would call it a “nonce”, but in England that word has… other
connotations. I’m hoping the community will adopt this alternative name instead.
(Unus is Latin for “Once”.)
🦔 Hashbert says: “This claim is fresh, specific, and meant for one destination. The Unus value must be unpredictable and unique, so a stale claim cannot simply wander back in wearing a fake moustache.”
Step 3
Hash the JSON and publish the result
Run a salted SHA-256 hash over your JSON and base-64 the result. Publish that string
as a one-line text file at the URL you listed in your JSON earlier.
The salt is fixed and exists to make sure that the hashes are only useful for this
exchange. Because the salt is not sent over the wire, we avoid the possibility of abusing
public hashing services.
The salt itself, along with how it was derived, is on the GitHub site.
🦔 Hashbert says: “The hash sits there, waiting to be retrieved, ready to exclaim "That Was Me!".”
Step 4
Make the HTTP request
Encode the bytes of your JSON with base-64 and add it to your request's headers as an
Authorization: Hashback header.
(You'd put the entire base-64 block as a single line without spaces. We've split it up on
this example for clarity.)
🦔 Hashbert says: “The header carries the claim. The verification hash confirms the claim is real.”
Step 5
The server verifies the hash
Now the server has your JSON, they can now check everything is right.
✅
The JSON is valid.
✅
The Host is correct.
✅
The Now timestamp is recent.
✅
The Verify URL belongs to a known user. (You.)
✅
The hash was retrieved over TLS with a known CA.
With the supplied verification hash in hand, the server may now repeat the salted SHA-256
and check it matches the expected verification hash. If they match, it must have come from
you!
1. Decode Read the JSON claim from the header.
2. Recalculate Hash the claim with the fixed salt.
3. Compare Accept an exact match.
🦔 Hashbert says: “A matching result confirms control of the registered website. The server has not been handed a long-lived secret but fresh proof from the right location. That is such a satisfactory conclusion it makes my spines wiggle.”