jhermsmeier / node-dkim

DomainKeys Identified Mail
MIT License
10 stars 11 forks source link

Relaxed verification issues #17

Open Synchro opened 3 years ago

Synchro commented 3 years ago

Hi πŸ‘‹Β β€“ I'm currently working on PHPMailer/DKIMValidator, running into troubles of my own, and took a look at yours to see how I compare!

I just saw this comment and have some candidates for the problem.

When unfolding headers, you replace the break and FWS with an empty string. This is incorrect. RFC5322 says:

Unfolding is accomplished by simply removing any CRLF that is immediately followed by FWS.

It's easy to get this wrong - it says to remove the CRLF, but that does not include the FWS that follows it. This means that this:

Subject: abc
    xyz

unfolds to Subject: abc xyz (with 4 spaces in the gap), not Subject: abcxyz.

The next canonicalization step collapses whitespace, so that line would become Subject: abc xyz (with 1 space).

Your next thought will be "but that means you end up with random spaces in the unfolded headers" – and you would be entirely correct. This can cause all sorts of problems – but so can collapsing the FWS to nothing, for example Subject: hello world might be folded to:

Subject: hello
 world

and unfolding it as you are would turn it into Subject: helloworld. This is a difficult problem to solve, and in fact the email RFCs don't have a good answer for it, especially in an edge case like a header that contains an unbroken char sequence of > 998 chars, as for example a signature might do. Fortunately there is a reasonable workaround, which is to use RFC2047 encoding, which allows you to fold an unbroken line into multiple lines, but the encoding means that when it's decoded, the unfolded FWS disappears. Apple Mail does this, and so does PHPMailer. Gmail and Outlook don't even try, and simply allow messages to break RFC.

Next I can hear you thinking, "but if a space appears in the middle of a DKIM signature, it will break the signature", and again, you're quite right, and this one had me baffled for ages, but there's a solution to this in DKIM:

Note that all whitespace, including SPACE, CR, and LF characters, MUST be encoded. After encoding, FWS MAY be added at arbitrary locations in order to avoid excessively long lines; such whitespace is NOT part of the value, and MUST be removed before decoding.

The upshot of this is while the canonicalized DKIM-Signature header may include arbitrary spaces after unfolding, those spaces should be removed before processing it.

Synchro commented 3 years ago

Another one. It might be convenient to rename the google DKIM signature header, but I would expect that to break that signature, because the original name is included when calculating the b value.

I sent myself a test message from gmail and found that the google signature uses the 1e100.net (it's a joke!) domain, which will never align with the envelope sender or from addresses, so I don't see any great value in verifying it anyway.

Synchro commented 3 years ago

There's a slight chicken and egg problem regarding removing b tag values. While a DKIM-Signature doesn't include its own b value in its own signature (because it can't!), it may sign any other DKIM-Signature headers in the message, and in those cases the b value of the other headers should be included in the calculation, as per DKIM:

Other DKIM-Signature header fields that are included in the signature should be treated as normal header fields; in particular, the "b=" tag is not treated specially.

So if you strip out b values in a blanket way across all the headers (as you're currently doing), and some signatures sign other signatures, they will break. To avoid this you need to know which signature you are calculating the b value for when canonicalizing the headers, so you can remove just that one and leave the others alone. The order signatures are presented is also important here. To be fair, I think this is a fairly rare edge case – while I've seen and used multiple signatures, I've never seen them sign each other.

Yes, I'm finding this painful too! I'm dreading dealing with signing absent headers...

immjs commented 2 years ago

Hello! I'm sorry for bumping but I think i may be getting an error related to this:

[
  {
    verified: false,
    status: 'PERMFAIL',
    error: Error: Body hash did not verify
        at C:\Users\infor\mailbox\node_modules\dkim\lib\verify-signature.js:58:22
        at QueryReqWrap.callback (C:\Users\infor\mailbox\node_modules\dkim\lib\get-key.js:80:5)
        at QueryReqWrap.onresolve [as oncomplete] (node:dns:257:10) {
      code: 'PERMFAIL'
    },
    signature: Signature {
      algorithm: 'rsa-sha256',
      canonical: 'relaxed/relaxed',
      copiedHeaders: [],
      domain: '1e100.net',
      expires: null,
      hash: <Buffer f2 0b fd a4 22 0b 3f 45 1a 36 82 58 a6 c0 2b b7 3b 59 7f 21 84 78 93 ce c4 8b 36 93 7a bf 12 fc>,
      headers: [Array],
      identity: null,
      length: null,
      query: 'dns/txt',
      selector: '20210112',
      signature: <Buffer 5f 18 25 e8 76 6e 7e 84 c4 1c 1f b4 ac d9 6f 2a e1 7f 6b 45 3a ea d5 83 72 3c fb 68 70 29 33 ed 7c a5 3a 07 77 2f b3 68 7e fc 4c 87 9f fb ee 07 51 37 ... 206 more bytes>,
      timestamp: null,
      version: '1'
    },
    key: Key {
      version: 'DKIM1',
      type: 'rsa',
      flags: null,
      granularity: null,
      hash: null,
      notes: null,
      service: null,
      key: <Buffer 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 f1 26 9b ab ec 82 d9 df 75 3e 45 93 12 48 dd 1f 60 ... 244 more bytes>
    }
  },
  {
    verified: false,
    status: 'PERMFAIL',
    error: Error: Body hash did not verify
        at C:\Users\infor\mailbox\node_modules\dkim\lib\verify-signature.js:58:22
        at QueryReqWrap.callback (C:\Users\infor\mailbox\node_modules\dkim\lib\get-key.js:80:5)
        at QueryReqWrap.onresolve [as oncomplete] (node:dns:257:10) {
      code: 'PERMFAIL'
    },
    signature: Signature {
      algorithm: 'rsa-sha256',
      canonical: 'relaxed/relaxed',
      copiedHeaders: [],
      domain: 'gmail.com',
      expires: null,
      hash: <Buffer f2 0b fd a4 22 0b 3f 45 1a 36 82 58 a6 c0 2b b7 3b 59 7f 21 84 78 93 ce c4 8b 36 93 7a bf 12 fc>,
      headers: [Array],
      identity: null,
      length: null,
      query: 'dns/txt',
      selector: '20210112',
      signature: <Buffer 0b e6 ef 40 53 c7 87 a8 6a 29 0c 7b f2 0f c9 80 2c ce f5 93 50 be 0f e0 24 44 85 f5 8d 60 2d 71 8b 1b 55 6f 30 54 5e bc 5f 13 fe f1 53 d2 e0 f1 f6 e6 ... 206 more bytes>,
      timestamp: null,
      version: '1'
    },
    key: Key {
      version: 'DKIM1',
      type: 'rsa',
      flags: null,
      granularity: null,
      hash: null,
      notes: null,
      service: null,
      key: <Buffer 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 01 01 00 ab c2 71 54 13 0b 1d 94 63 d5 6b c8 31 21 c0 a5 16 ... 244 more bytes>
    }
  }
]

This is my email:

Received: by mail-yw1-f179.google.com with SMTP id 00721157ae682-3135519f95fso31357977b3.6
        for <stuff@89-95-197-91.abo.bbox.fr>; Sun, 12 Jun 2022 10:18:39 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=gmail.com; s=20210112;
        h=mime-version:from:date:message-id:subject:to;
        bh=8gv9pCILP0UaNoJYpsArtztZfyGEeJPOxIs2k3q/Evw=;
        b=C+bvQFPHh6hqKQx78g/JgCzO9ZNQvg/gJESF9Y1gLXGLG1VvMFRevF8T/vFT0uDx9u
         Y+cHDnlFyoNz+b2MPBum62801IrNjZOiOqdicDg4YMyJj7oBc0MGzO97uXai5wFnQ1is
         2jD44KthXM3cIz39VlNWNDImC39A8cigoftK1dxBN1v8sTAKlhVwMxSbJj3z1YL1RXpa
         SMwMpeGZlHHo7wZA1kfxAKCFIZE4/jIR7CdSBAcq05l/0DlMb91513Z/li/pIjQ/Yn/o
         t5Ttn5lq0uNU99RiBIYYQs/vmbiyjYyTKikCi/tBWkJ/XdGQMvMrp02GaeL8aUdkVKeN
         bi1Q==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20210112;
        h=x-gm-message-state:mime-version:from:date:message-id:subject:to;
        bh=8gv9pCILP0UaNoJYpsArtztZfyGEeJPOxIs2k3q/Evw=;
        b=Xxgl6HZufoTEHB+0rNlvKuF/a0U66tWDcjz7aHApM+18pToHdy+zaH78TIef++4HUT
         e0xP2W6O2rrK9gYmZvIjuididyCv/F51x9DaArfFP3o0rH8bNomojEhhiD8mm4FtPzNe
         bkUjsPqziOZ4XIT7R2pKmqXee5LYBs8LheIfjNfFx7NZWChUlVOs7YxRifXMMm8YTXYt
         KJ4tYKHm+JAg5Kur2AuTOTQq0F1lab7TE/GpcDdQ6xoPw46iJbeD2KyrEz/cMZadLjJQ
         7NN3WOqxSyqCOeuF2QvKmQiZPLDRvbK5PdLvLbLoeIea+iGKvqeSc7jqTJ711RY0nC2D
         uEwg==
X-Gm-Message-State: AOAM533TcQ8isQ2Ug/9A2Hyv1h2Ce91s0/wjR6+FoRlevMDobJnYKZco
        Nj5lhqMn/rO/ZkkZnC/Q2Q+bIj/tXhZPEbapOviAhjUR7Go=
X-Google-Smtp-Source: ABdhPJznI/0ojIL3H3QSTWVb9uLaHtvVSXgllKfOkmi83IyDmmb+tjgB982vUlol0Re2p7g8vt7JZwNajg0dJAMC/34=
X-Received: by 2002:a81:b652:0:b0:30c:480d:96d2 with SMTP id
 h18-20020a81b652000000b0030c480d96d2mr59831154ywk.170.1655054318564; Sun, 12
 Jun 2022 10:18:38 -0700 (PDT)
MIME-Version: 1.0
From: Romie Greenbourg <informathemusic@gmail.com>
Date: Sun, 12 Jun 2022 19:18:28 +0200
Message-ID: <CAADMp1OMrRAP5g7RoJLnH+p9O3sb=HC7Mb-05hY2H7jtvHcX8Q@mail.gmail.com>
Subject: asdf
To: stuff@89-95-197-91.abo.bbox.fr
Content-Type: multipart/alternative; boundary="000000000000bdfa3e05e1435ce3"

--000000000000bdfa3e05e1435ce3
Content-Type: text/plain; charset="UTF-8"

asdfasdfasdfasdfa

--000000000000bdfa3e05e1435ce3
Content-Type: text/html; charset="UTF-8"

<div dir="ltr">asdfasdfasdfasdfa</div>

--000000000000bdfa3e05e1435ce3--
.
jhermsmeier commented 2 years ago

@immjs no worries, tbh this has dropped off my radar a bit, so kinda good you bumped it – thanks for posting the email causing it! Will have a look!