如何可靠地散列JavaScript对象?

nh2*_*nh2 28 javascript hash json node.js

有没有一种可靠的方法来JSON.stringify一个JavaScript对象,保证所有浏览器,node.js等的ceated JSON字符串是相同的,因为Javascript对象是相同的?

我想哈希JS对象

{
  signed_data: object_to_sign,
  signature:   md5(JSON.stringify(object_to_sign) + secret_code)
}
Run Code Online (Sandbox Code Playgroud)

并将它们传递给Web应用程序(例如Python和node.js)和用户,以便用户可以对一个服务进行身份验证,并显示该服务的下一个服务"签名数据",以检查数据是否可信.

但是,我遇到了JSON.stringify在实现中并不是唯一的问题:

  • 在node.js/V8中,JSON.stringify返回没有不必要的空格的JSON字符串,例如"{"user_id":3}.
  • Python的simplejson.dumps留下了一些空格,例如'{"user_id":3}'
  • 可能其他stringify实现可能会对空格,属性的顺序或其他方面做出不同的处理.

有可靠的跨平台stringify方法吗?有没有"正规化的JSON"?

你会推荐其他方法来散列像这样的对象吗?

更新:

这是我用作解决方法的方法:

normalised_json_data = JSON.stringify(object_to_sign)
{
  signed_data: normalised_json_data,
  signature:   md5(normalised_json_data + secret_code)
}
Run Code Online (Sandbox Code Playgroud)

因此,在这种方法中,不是对象本身,而是对其JSON表示(特定于sigining平台)进行签名.这很好用,因为我现在签名的是一个明确的字符串,我可以在检查签名哈希后轻松地JSON.parse数据.

这里的缺点是,如果我将整个{signed_data,signature}对象作为JSON发送,我必须两次调用JSON.parse并且它看起来不太好,因为内部对象被转义:

{"signature": "1c3763890298f5711c8b2ea4eb4c8833", "signed_data": "{\"user_id\":5}"}
Run Code Online (Sandbox Code Playgroud)

Fro*_*y Z 35

您可能对npm package object-hash感兴趣,它似乎具有相当好的活动和可靠性级别.

var hash = require('object-hash');

var testobj1 = {a: 1, b: 2};
var testobj2 = {b: 2, a: 1};
var testobj3 = {b: 2, a: "1"};

console.log(hash(testobj1)); // 214e9967a58b9eb94f4348d001233ab1b8b67a17
console.log(hash(testobj2)); // 214e9967a58b9eb94f4348d001233ab1b8b67a17
console.log(hash(testobj3)); // 4a575d3a96675c37ddcebabd8a1fea40bc19e862
Run Code Online (Sandbox Code Playgroud)

  • 不过,这并不是跨平台的,这是OP的要求。还有一些围绕JSON的确定性版本包装器,其运行速度比对象哈希更快。请参阅我制定的基准测试:https://jsperf.com/stringify-vs-object-hash (2认同)

Mat*_*ter 8

这是一个老问题,但我想我会为任何谷歌裁判添加一个当前的解决方案.

现在签名和散列JSON对象的最佳方法是使用JSON Web令牌.这允许对象基于签名进行签名,散列,然后由其他人验证.它提供了一系列不同的技术,并拥有一个活跃的开发小组.

  • 尝试生成 jwt 签名,但看起来它不会忽略有效负载 json 中的字段顺序。所以我不明白它有什么帮助,因为OP想在哈希计算期间忽略字段顺序。您能解释一下您的想法吗? (3认同)
  • 他在第三项中提到了“stringify”的缺点,即生成的字符串可以具有不同的字段顺序。我仍然不明白 JWT 如何为 `{"foo":"bar", "bar":"foo"}` 和 `{"bar":"foo","foo":" 生成相同的哈希/签名bar"}` 除了字段顺序之外基本相同。如果 JWT 没有这样的东西,那么你的答案并不是 OP(和我)正在寻找的确切答案。 (3认同)
  • 不要混淆签名和哈希,它们可以解决不同的问题。签名就像是对类固醇的哈希,重点是安全性。哈希与安全无关。随着您自动获得安全的哈希,验证,到期和信任,JWT可能是针对OP问题的最佳解决方案。每次都会为同一个对象获得不同的签名,因为时间戳会自动包含在散列数据中。听起来@Derp只是希望逻辑上相同的对象表示形式之间具有相同的哈希,在这种情况下,请参阅“ object-hash” (2认同)

isg*_*oed 8

在尝试了一些哈希算法和 JSON 到字符串的方法之后,我发现这是最好的(抱歉,它是打字稿,当然可以重写为 javascript):

// From: /sf/ask/382699061/
function sortObjectKeys(obj){
    if(obj == null || obj == undefined){
        return obj;
    }
    if(typeof obj != 'object'){ // it is a primitive: number/string (in an array)
        return obj;
    }
    return Object.keys(obj).sort().reduce((acc,key)=>{
        if (Array.isArray(obj[key])){
            acc[key]=obj[key].map(sortObjectKeys);
        }
        else if (typeof obj[key] === 'object'){
            acc[key]=sortObjectKeys(obj[key]);
        }
        else{
            acc[key]=obj[key];
        }
        return acc;
    },{});
}
let xxhash64_ObjectToUniqueStringNoWhiteSpace = function(Obj : any)
{
    let SortedObject : any = sortObjectKeys(Obj);
    let jsonstring = JSON.stringify(SortedObject, function(k, v) { return v === undefined ? "undef" : v; });

    // Remove all whitespace
    let jsonstringNoWhitespace :string = jsonstring.replace(/\s+/g, '');

    let JSONBuffer: Buffer = Buffer.from(jsonstringNoWhitespace,'binary');   // encoding: encoding to use, optional.  Default is 'utf8'
    return xxhash.hash64(JSONBuffer, 0xCAFEBABE, "hex");
}
Run Code Online (Sandbox Code Playgroud)

它使用了npm模块:https://cyan4973.github.io/xxHash/,https : //www.npmjs.com/package/xxhash

好处:

  • 这是确定性的
  • 忽略按键顺序(保留数组顺序)
  • 跨平台(如果您能找到 JSON-stringify 的等效项)JSON-stringify 希望不会获得不同的实现,并且删除空格有望使其独立于 JSON 格式。
  • 64位
  • 十六进制字符串结果
  • 最快(2177 B JSON 为 0.021 毫秒,150 kB JSON 为 2.64 毫秒)


Mar*_*ahn 7

你要求跨多种语言的实现是相同的...你几乎肯定是运气不好.您有两种选择:

  • 检查www.json.org实施,看看它们是否可能更加标准化
  • 在每种语言中使用自己的语言(使用json.org实现作为基础,应该做很少的工作)


Gre*_*ill 5

您可以stringify()通过应用以下规则来规范化结果:

  • 删除不必要的空格
  • 对哈希中的属性名称进行排序
  • 定义明确的一致报价方式
  • 规范化字符串内容(因此“ \ u0041”和“ A”变为相同)

这将为您提供对象的规范JSON表示形式,然后可以可靠地对其进行哈希处理。