Andrew Davis
Posted on January 17, 2020
I recently was asked to explain some code that used an HMAC signature for authenticating/verifying a message. While I understood that HMACs are used for message authenticity, I did not fully grasp the implementation. Here is the information I have discovered through more research.
MAC stands for "message authentication code". Essentially, it is a way to generate a code to be submitted with a message that verifies the sender of the message and also verifies the message has not been modified. HMAC stands for "hash-based message authentication code". It uses a cryptographic hashing algorithm to generate the MAC. An HMAC algorithm works by hashing a message along with a secret key. The resulting hash is called a signature or digest. The message is then transferred to a recipient along with the signature. If the recipient has the secret key, they can hash the message with the same algorithm and verify the resulting signature matches the one sent with the message. As a result, the message is simultaneously authenticated and its integrity verified.
We can try to better understand an HMAC through an example. Let us imagine that you have a doctor's office application that needs to send an HTTP request to a secure API that contains medical history for patients. The message you want to send to the API is a JSON packet containing the results of the latest visit to the doctor by the patient.
{
"first_name": "Andrew",
"last_name": "Davis",
"visit": "2020-01-12 09:56:12",
"symptoms": [
"sore throat",
"runny nose",
"sneezing",
"headache"
],
"diagnosis": "Common Cold",
"suggested_treatment": [
"drink water",
"rest",
"consume ibuprofen twice a day"
]
}
The API has provided you with a secret key that might look something like this 13441c78e4440683a9e32aa7e4ee39fd3544b87968cc5a30f621f7e32029b2f5
and says you must use it to create a SHA-256 HMAC signature to pass along in the Authorization
HTTP header for requests.
Our programming language of choice is PHP. Thankfully, PHP already has functions for generating HMAC signatures. To see all the supported algorithms, you can run hash_hmac_algos()
and PHP will return an array of all the hash types supported. Here is the list for PHP 7.4 on my machine.
>>> hash_hmac_algos();
=> [
"md2",
"md4",
"md5",
"sha1",
"sha224",
"sha256",
"sha384",
"sha512/224",
"sha512/256",
"sha512",
"sha3-224",
"sha3-256",
"sha3-384",
"sha3-512",
"ripemd128",
"ripemd160",
"ripemd256",
"ripemd320",
"whirlpool",
"tiger128,3",
"tiger160,3",
"tiger192,3",
"tiger128,4",
"tiger160,4",
"tiger192,4",
"snefru",
"snefru256",
"gost",
"gost-crypto"
"haval128,3",
"haval160,3",
"haval192,3",
"haval224,3",
"haval256,3",
"haval128,4",
"haval160,4",
"haval192,4",
"haval224,4",
"haval256,4",
"haval128,5",
"haval160,5",
"haval192,5",
"haval224,5",
"haval256,5",
]
We see that SHA-256 is supported by PHP. SHA-256 is a common hashing algorithm created by the Unites States' NSA which is often used with HMAC algorithms.
Now, we need to create the actual HMAC signature of our message. To do so, we can use PHP's hash_hmac
function. The first parameter is the algorithm, the second parameter is the message and the third parameter is the secret key.
>>> hash_hmac('sha256',\
... '{"first_name": "Andrew","last_name": "Davis","visit": "2020-01-12 09:56:12","symptoms": ["sore throat","runny nose","sneezing","headache"],"diagnosis": "Common Cold","suggested_treatment": ["drink water","rest","consume ibuprofen twice a day"]}',\
... '13441c78e4440683a9e32aa7e4ee39fd3544b87968cc5a30f621f7e32029b2f5');
=> "0def47f4878406abf864da0d34f9e3627653317e7c77da8230e0f65b52c09479"
PHP responds with a message digest/signature: 0def47f4878406abf864da0d34f9e3627653317e7c77da8230e0f65b52c09479
. We can submit the JSON message to the API along with the signature. The API will then take the message and it will create an HMAC signature using SHA-256 and its secret key. If its resulting HMAC signature matches the HMAC you provided, then it accepts the message for processing.
The API exchange highlights the two main benefits of HMAC.
First, since an HMAC requires a secret key, both the sender and recipient have to have the same secret key for HMAC verification to work. As a result, the HMAC acts similarly to a password. It is very important to keep the secret key safe. Since it is used like a password, it needs to be saved in encrypted storage. It is also important to use a sufficiently long secret key that cannot be brute forced by password breakers.
Second, an HMAC is based not only on the secret key, but also on the message itself. If two HMACs are created with the same secret key, but different messages, the resulting codes will be different. When the API sees that its HMAC matches the HMAC in the request, it knows the request was not modified during transport. Since hackers will try to modify messages in between clients and servers, HMAC signatures can be a powerful tool to verify authenticity of requests.
Hopefully this explanation has shed some light on HMAC signatures. Unfortunately, I do not have the math and cryptography experience to explain how the HMAC algorithm works to create the signature. Maybe someone else in the DEV Community can provide a layman's explanation of the computation.
What is your experience with HMAC and where have you seen it used it effectively?
Posted on January 17, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.