I'm trying to do an HMAC SHA256 hash of stringified data using both JavaScript (CryptoJS Libraries) and PHP (built in HMAC function). I'm concerned that the JavaScript JSON.stringify will not be consistent/identical to the PHP json_encode() function. Is there a better approach to this stringifying of the data (object/array)?
Here's my test, which works. But, I'm concerned about Spanish characters and other encodings/entities that the code may encounter.
<h1>Testing HMAC Javascript to PHP Comparison</h1>
<br><br>
<div id="php_mac">
<?php
// Testing HMAC
$security_key = '0123456789';
$obj = array(
'field1' => 1,
'field2' => '2',
'field3' => "'",
);
// Calculate HMAC SHA256
$str_data = json_encode($obj);
echo "PHP str_data: ".$str_data."<br>";
$hash = hash_hmac('sha256', $str_data, $security_key, true);
$hashInBase64 = base64_encode($hash);
echo "PHP hashInBase64: ".$hashInBase64;
?>
</div>
<br><br>
<div id="javascipt_hmac">
<div id="javascript_str_data"></div>
<div id="javascript_hashInBase64"></div>
<script>
var security_key = '0123456789';
var obj = {
'field1': 1,
'field2': '2',
'field3': "'",
};
// Create security hash based on posted data
var str_data = JSON.stringify(obj);
$('#javascript_str_data').html('str_data: '+str_data);
// Using CryptoJS to HMAC SHA256 the str_data
var hash = CryptoJS.HmacSHA256(str_data, security_key);
var hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
$('#javascript_hashInBase64').html('JS hashInBase64: '+hashInBase64)
</script>
</div>
Additional thoughts: I'm worried about spacing/quoting differences with JSON methods. Perhaps I should loop thru the object/array and use the "values" only to produce the string of data to be HMAC'ed? Assuming this can be kept to a single array/object, that should produce a consistent "values" string. But, then how do you keep a consistent ordering. I assume it could be ordered by key first.
As @Pointy mentioned in the comments, the output of JSON.stringify
and json_encode
may have slight differences in two scenarios:
On "simple" values, the PHP documentation has this to say:
Like the reference JSON encoder, json_encode() will generate JSON that is a simple value (that is, neither an object nor an array) if given a
string
,integer
,float
orboolean
as an inputvalue
. While most decoders will accept these values as valid JSON, some may not, as the specification is ambiguous on this point.
To summarise, always test that your JSON decoder can handle the output you generate from json_encode().
If you're worried about the data being 100% faithfully recreated, consider encoding the data before storing it (i.e. base64_encode it).
P.S. If you're going to HMAC the data, you need to 1) only HMAC the values and 2) make sure you always access the values in the same order every time because JSON makes no ordering promises for anything except arrays.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With