5

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.

2
  • The serialized output might be the same, but it might not be. The only requirement for a working JSON encoder is that the result be recognizable as JSON when parsed. White space, for example, can vary, as can the way the encoder encodes characters outside the 7-bit ASCII range. Even between two different JavaScript implementations you might see differences. Commented Feb 2, 2016 at 15:04
  • 1
    In the XML context, I think digest computation usually involves stipulations on exactly how tag (and attribute) contents should be treated. Seems like some approach like that would be more reliable. Commented Feb 2, 2016 at 15:06

1 Answer 1

2

As @Pointy mentioned in the comments, the output of JSON.stringify and json_encode may have slight differences in two scenarios:

  1. An object's key/value ordering (objects are unordered)
  2. "Simple" values

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 or boolean as an input value. 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.

Sign up to request clarification or add additional context in comments.

1 Comment

It is for an HMAC SHA256 content hash for security verification. I'll stick to using specific ID's (GUIDs and Numerics) in the content string creation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.