web-dev-qa-db-ja.com

JavaScriptオブジェクトを確実にハッシュする方法は?

JavaScriptオブジェクトが同じであれば、作成されたJSON文字列がすべてのブラウザー、node.jsなどで同じであることを保証するJavaScriptオブジェクトをJSON.stringifyする信頼できる方法はありますか?

私はJSオブジェクトをハッシュしたい

{
  signed_data: object_to_sign,
  signature:   md5(JSON.stringify(object_to_sign) + secret_code)
}

そして、それらをWebアプリケーション(Python and node.js)など)とユーザー全体に渡して、ユーザーが1つのサービスに対して認証し、そのサービスの次のサービスの「署名付きデータ」を確認できるようにします。データが本物である場合。

しかし、JSON.stringifyは実装全体で実際には一意ではないという問題に遭遇しました。

  • Node.js/V8では、JSON.stringifyは '{"user_id":3}などの不要な空白なしのJSON文字列を返します。
  • Pythonのsimplejson.dumpsは、いくつかの空白を残します。 '{"user_id":3}'
  • おそらく、他のstringify実装は、空白、属性の順序などを異なる方法で処理する可能性があります。

信頼できるクロスプラットフォームの文字列化メソッドはありますか? 「正規化されたJSON」はありますか?

このようなオブジェクトをハッシュする他の方法をお勧めしますか?

更新:

これは私が回避策として使用するものです:

normalised_json_data = JSON.stringify(object_to_sign)
{
  signed_data: normalised_json_data,
  signature:   md5(normalised_json_data + secret_code)
}

したがって、このアプローチでは、オブジェクト自体ではなく、JSON表現(署名プラットフォームに固有)が署名されます。これでうまくいきます。今署名するのは一義的な文字列であり、署名ハッシュを確認した後でデータを簡単にJSON.parseできるからです。

ここでの欠点は、{signed_data、signature}オブジェクト全体をJSONとしても送信する場合、JSON.parseを2回呼び出す必要があり、内部のオブジェクトがエスケープされるため、JSON.parseのように見えないことです。

{"signature": "1c3763890298f5711c8b2ea4eb4c8833", "signed_data": "{\"user_id\":5}"}
28
nh2

あなたは複数の言語にわたる何かの実装が同じであることを求めています...あなたはほぼ確実に運が悪いです。次の2つのオプションがあります。

  • www.json.orgの実装をチェックして、より標準化されているかどうかを確認する
  • 各言語で独自のものを作成する(json.org実装をベースとして使用し、実行する作業はほとんどありません)
6
Mark Kahn

Npmパッケージ 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
32
Frosty Z

これは古い質問ですが、Googleのレフリーのために、この質問に現在の解決策を追加したいと思いました。

JSONオブジェクトに署名してハッシュする最善の方法は、 JSON Web Tokens を使用することです。これにより、オブジェクトは署名され、ハッシュされ、署名に基づいて他のユーザーによって検証されます。さまざまなテクノロジーに対応しており、活発な開発グループがあります。

7
Matt Forster

次のようなルールを適用することにより、stringify()の結果を正規化できます。

  • 不要な空白を削除する
  • 属性名をハッシュでソートする
  • 明確に定義された一貫した引用スタイル
  • 文字列の内容を正規化する( "\ u0041"と "A"は同じになる)

これにより、オブジェクトの正規のJSON表現が残り、確実にハッシュできます。

5
Greg Hewgill

いくつかのハッシュアルゴリズムとJSON-to-stringメソッドを試した後、これが最適に機能することがわかりました(申し訳ありませんが、それはTypeScriptです。もちろん、JavaScriptに書き換えることができます)。

// From: https://stackoverflow.com/questions/5467129/sort-javascript-object-by-key
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");
}

Npmモジュールを使用しました: https://cyan4973.github.io/xxHash/https://www.npmjs.com/package/xxhash

メリット:

  • これは確定的です
  • キーの順序を無視する(配列の順序を保持)
  • クロスプラットフォーム(JSON-stringifyに相当するものを見つけることができる場合)JSON-stringifyは別の実装にならないことが期待され、空白の削除はJSON-formattingから独立させると期待されます。
  • 64ビット
  • 16進数文字列の結果
  • 最速(2177 B JSONの場合は0.021 ms、150 kB JSONの場合は2.64 ms)
0
isgoed

bencode がニーズに適している場合があります。これはクロスプラットフォームであり、エンコーディングはすべての実装で同じであることが保証されています。

欠点は、nullまたはブール値をサポートしないことです。しかし、たとえばブール値-> 0|1とnull-> "null"エンコードする前。

0
spiffytech