Live data from Hacker News

How (not) to sign a JSON object (2019)

latacora.com

21–30 of 45 posts

Re: How (not) to sign a JSON object (2019)

#21
Another problem with signing JSON: you can have two different json objects that mean the same thing, and will do exactly the same thing in your code e.g. {"a": "foo", "b": "bar"} vs {"b": "bar", "a": "foo"}. Also, whitespace. Are there any standards for normalising json, so that two equivalent, but differently written JSON files will have the same signature?

Re: How (not) to sign a JSON object (2019)

#22
> Unless you have a good reason why you need an (asymmetric) signature, you want a MAC.

Is "I want the server/validating side to be safe even against server-side attackers with read-only permissions" not a good reason? Because that's one thing that asymmetric signatures provide out of the box compared to MACs.

Re: How (not) to sign a JSON object (2019)

#23

Another problem with signing JSON: you can have two different json objects that mean the same thing, and will do exactly the same thing in your code e.g. {"a": "foo", "b": "bar"} vs {"b": "bar", "a": "foo"}. Also, whitespace. Are there any standards for normalising json, so that two equivalent, but differently written JSON files will have the same signature?

This is why, in one of my projects, I first stringified the JSON using built in JSON.stringify(your_json) function, then signed that string and sent the string, its signature, and public key to server. Server verifies the signature using the string and if passes, then uses JSON.parse(your_string) to get the original json.

Re: How (not) to sign a JSON object (2019)

#24

Another problem with signing JSON: you can have two different json objects that mean the same thing, and will do exactly the same thing in your code e.g. {"a": "foo", "b": "bar"} vs {"b": "bar", "a": "foo"}. Also, whitespace. Are there any standards for normalising json, so that two equivalent, but differently written JSON files will have the same signature?

That's canonicalization, and the article does mention it (but unfortunately does not offer much insight other than that it's hard).

Re: How (not) to sign a JSON object (2019)

#26
post #18
post #15

Earlier quoted context omitted.

Thanks for explaining the background of that. I agree that human readability can make things somewhat easier. The part about a binary format for the signatures confuses me a little - the signatures are going to be big no matter what. If you care about minimizing size would it make sense to just find a more efficient way to encode the signatures rather than change the entire message format?

Coze uses base64 encoding for binary values such as `tmb` and `sig`. `tmb` isn't a problem since digests are designed to be short, but signatures for some primitive might be very large. When compared to encoding a value directly in binary, base64 has about a 25% overhead (6 bits /8 bits, 3/4). As far as the concern about using better encoding, base64 is just about as good as it gets while being maximally compatible.…

> When compared to encoding a value directly in binary, base64 has about a 25% overhead (6 bits /8 bits, 3/4).

The original data take ≥25% less space than the base64-encoded data, but the base64-encoded data take ≥33⅓% more space than the original data. The overhead is about 33⅓%.

Re: How (not) to sign a JSON object (2019)

#27
post #26
post #18

Earlier quoted context omitted.

Coze uses base64 encoding for binary values such as `tmb` and `sig`. `tmb` isn't a problem since digests are designed to be short, but signatures for some primitive might be very large. When compared to encoding a value directly in binary, base64 has about a 25% overhead (6 bits /8 bits, 3/4). As far as the concern about using better encoding, base64 is just about as good as it gets while being maximally compatible.…

> When compared to encoding a value directly in binary, base64 has about a 25% overhead (6 bits /8 bits, 3/4). The original data take ≥25% less space than the base64-encoded data, but the base64-encoded data take ≥33⅓% more space than the original data. The overhead is about 33⅓%.

That's correct.

Re: How (not) to sign a JSON object (2019)

#28
post #17

I have a feeling that idea 2 is a recipe for disaster: > Add the tag and the exact string you signed to the object, validate the signature and then validate that the JSON object is the same as the one you got. In cryptographic practice, redundant information usually spells disaster, because inevitably, someone will use the copy that wasn't verified. But let's dig into it. If I understand this correctly, the suggestio…

This sort of suggestion is for that worst case that you already have brownfield consumers that don't care about the signature using outer fields and you need to add the signature without breaking those consumers.

The redundancy is absolutely a recipe for disaster, but so is the part where you have brownfield consumers that you can't break and know that they also don't care about message security.

Unfortunately, it's an all too common brownfield to find yourself stepping into, which is why it is such a too common ask for "inline JSON signatures" (or other document languages like XML) that don't change the outer shape of the JSON document to break backwards compatibility with dumber consumers.

Also, unfortunately the most correct answer in cryptographic practice is also often the hardest to sell to those consumers (or to business people prioritizing changes to them): break those consumers and force them to care about security so that a rising tide lifts all boats.

Re: How (not) to sign a JSON object (2019)

#29

Another problem with signing JSON: you can have two different json objects that mean the same thing, and will do exactly the same thing in your code e.g. {"a": "foo", "b": "bar"} vs {"b": "bar", "a": "foo"}. Also, whitespace. Are there any standards for normalising json, so that two equivalent, but differently written JSON files will have the same signature?

This is why, in one of my projects, I first stringified the JSON using built in JSON.stringify(your_json) function, then signed that string and sent the string, its signature, and public key to server. Server verifies the signature using the string and if passes, then uses JSON.parse(your_string) to get the original json.

The problem is the following two lines produce different outputs, despite having content that means the same thing:

    console.log(JSON.stringify({ x: 5, y: 6 }));
    console.log(JSON.stringify({ y: 6, x: 5 }));
Post reply on HN