Hacker News new | ask | show | jobs
by codeflo 496 days ago
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 suggestion is to have something like this:

    {
        "object": "with",
        "some": "properties",

        "signatureInfo": {
            "signedString": "{\"object\":\"with\",\"some\":\"properties\"}",
            "signature": "... base64-encoded-signature ..."
        }
    }
It's mentioned that "the downside is your messages are about twice the size that they need to be". In my opinion, this scheme is pointless. To verify "the JSON object is the same as the one you got", you have to do what?

1. Parse the outer object as JSON, extract and remove signatureInfo.

2. Verify the signature.

3. Parse the signedString as JSON.

4. Verify that the object you got in step 1 equal to object you got in step 3 using a some kind of deep equality.

First of all, this is error prone, and as underspecified as JSON is, there are potential exploits if the comparison isn't done carefully. But even worse, if you think about it, the outer JSON is entirely useless, since you need to parse the inner JSON anyway -- so why not just use it directly?

It seems to me that this suggestion is strictly worse than just sending the inner part:

    {
        "signedString": "{\"object\":\"with\",\"some\":\"properties\"}",
        "signature": "... base64-encoded-signature ..."
    }
Yes, it's no longer "in-band", but I don't think it was really in-band before, it was just out-of-band with an outer layer of redundant information.
2 comments

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.

Yep. I had a dickens of a time making XML-DSIG secure. I don’t know how they didn’t realize that getElementById returns the first element with an id and doesn’t give a shit if there are multiples. If you chose a different parent element you can get different results. I had to roll my own that threw an error on duplicate IDs and rejected the document.

If you only expect one signature I would recommend you wrap the signed content instead of treating it as a sibling. And even if you have multiple, maybe have signatures be siblings but put them all in the same wrapper. This means the recipient has to know signatures exist but honestly tough shit. If you’re adding sigs you’re going to end up expecting them and that’s a fact not an opinion. You don’t want any tools that ignore the signature and make decisions without validating them first. That’s a Confused Deputy attack waiting to happen.

Also in XML you have to canonicalize the document first, so that any formatting changes don’t invalidate the signature. So a couple other parts of what you said are true but there are solutions, even if annoying ones.