web-dev-qa-db-fra.com

Comment hacher de manière fiable des objets JavaScript?

Existe-t-il un moyen fiable de JSON.stringifier un objet JavaScript qui garantit que la chaîne JSON créée est la même sur tous les navigateurs, node.js et ainsi de suite, étant donné que l'objet Javascript est le même?

Je veux hacher des objets JS comme

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

et les transmettre à travers les applications Web (par exemple Python et node.js) et l'utilisateur afin que l'utilisateur puisse s'authentifier auprès d'un service et afficher le prochain service "données signées" pour celui-ci à vérifier si les données sont authentiques.

Cependant, je suis tombé sur le problème que JSON.stringify n'est pas vraiment unique à travers les implémentations:

  • Dans node.js/V8, JSON.stringify renvoie une chaîne JSON sans espaces blancs inutiles, tels que '{"user_id": 3}.
  • Simplejson.dumps de Python laisse un espace, par exemple '{"user_id": 3}'
  • D'autres implémentations stringify pourraient probablement traiter différemment les espaces blancs, l'ordre des attributs ou autre.

Existe-t-il une méthode de filtrage multiplateforme fiable? Existe-t-il un "JSON nomalisé"?

Recommanderiez-vous d'autres façons de hacher des objets comme celui-ci?

MISE À JOUR:

Voici ce que j'utilise comme solution de contournement:

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

Donc, dans cette approche, pas l'objet lui-même, mais sa représentation JSON (qui est spécifique à la plate-forme de signature) est signée. Cela fonctionne bien car ce que je signe maintenant est une chaîne sans ambiguïté et je peux facilement JSON.parse les données après avoir vérifié le hachage de signature.

L'inconvénient ici est que si j'envoie tout l'objet {signed_data, signature} sous forme de JSON, je dois appeler JSON.parse deux fois et il ne semble pas aussi agréable, car l'intérieur est échappé:

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

Vous demandez qu'une implémentation de quelque chose dans plusieurs langues soit la même ... vous n'avez presque certainement pas de chance. Vous avez deux options:

  • consultez les implémentations de www.json.org pour voir si elles pourraient être plus standardisées
  • lancez le vôtre dans chaque langue (utilisez les implémentations de json.org comme base et il devrait y avoir TRÈS peu de travail à faire)
6
Mark Kahn

Vous pourriez être intéressé par le paquet npm hash objet , qui semble avoir un assez bon niveau d'activité et de fiabilité.

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

C'est une vieille question, mais je pensais que j'ajouterais une solution actuelle à cette question pour tous les arbitres Google.

La meilleure façon de signer et de hacher des objets JSON est maintenant d'utiliser JSON Web Tokens . Cela permet à un objet d'être signé, haché puis vérifié par d'autres sur la base de la signature. Il est offert pour un tas de technologies différentes et dispose d'un groupe de développement actif.

7
Matt Forster

Vous pouvez normaliser le résultat de stringify() en appliquant des règles telles que:

  • supprimer les espaces inutiles
  • trier les noms d'attributs dans des hachages
  • style de devis cohérent bien défini
  • normaliser le contenu des chaînes (donc "\ u0041" et "A" deviennent les mêmes)

Cela vous laisserait une représentation canonique JSON de votre objet, que vous pouvez ensuite hacher de manière fiable.

5
Greg Hewgill

Après avoir essayé des algorithmes de hachage et des méthodes JSON en chaîne, j'ai trouvé que cela fonctionnait le mieux (Désolé, c'est TypeScript, peut bien sûr être réécrit en 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");
}

Il a utilisé le module npm: https://cyan4973.github.io/xxHash/ , https://www.npmjs.com/package/xxhash

Les avantages:

  • C'est déterministe
  • Ignore l'ordre des clés (préserve l'ordre des tableaux)
  • Multiplateforme (si vous pouvez trouver des équivalents pour JSON-stringify) JSON-stringify, espérons-le, n'obtiendra pas une implémentation différente et la suppression des espaces rendra, espérons-le, le formatage JSON indépendant.
  • 64 bits
  • Chaîne hexadécimale un résultat
  • Le plus rapide (0,021 ms pour 2177 B JSON, 2,64 ms pour 150 kB JSON)
0
isgoed

Vous pouvez trouver bencode adapté à vos besoins. Il est multiplateforme et le codage est garanti d'être le même à partir de chaque implémentation.

L'inconvénient est qu'il ne prend pas en charge les valeurs nulles ou booléennes. Mais cela peut vous convenir si vous faites quelque chose comme transformer, par exemple, bools -> 0|1 et null -> "null" avant l'encodage.

0
spiffytech