要約

  • RFC 6376の任意tag l=は、body canonicalization後の先頭何octetsをhashへ入れるかを指定する。限界より後はDKIM検証の対象外であり、signatureが成功しても変わらない。
  • headerには別の境界がある。h=は署名したheader field名を順番付きで示し、同名fieldが複数ある場合はどのinstanceを選んだかが結果を左右する。
  • 再現可能なreceiptは、raw message、対象signature、canonicalization、header instance map、prefixとtailの長さ・hash、観測したDNS key、verifier、信頼済みAuthentication-Resultsの境界を保存する。

passの右側に残ったbytes

mail systemの画面に緑のpassが一つある。その裏の計算では、canonicalized bodyが17,800 octets、l=13,000だったとする。Verifierが確認したのは最初の13,000 octetsであり、残る4,800 octetsではない。

そのtailはmailing listの案内かもしれない。archive systemが付けた印かもしれず、MIMEの見え方を変える追加かもしれない。攻撃者が挿入した内容である可能性もある。signature resultは、これらを分類しない。tailは「invalid」ではなく、そのsignatureにとって「unvalidated」である。

RFC 6376は、この境界を仕様として定義する。l=を省けばcanonicalized body全体が対象になる。指定すれば、body開始点からその数のoctetsだけがhashへ入る。限界後のdataはDKIMによってvalidateされない。l=0ならbodyは完全にunsignedである。

受信側policyは、部分署名を受け入れない選択ができる。その場合もcryptographic resultとpolicy dispositionを別々に記録すべきだ。計算がscope内で成功したことと、systemがmessageを安全または許容可能と判定したことは同じではない。

Murray Kucherawyの位置もdocument単位で限定する必要がある。RFC 6376はDave Crocker、Tony Hansen、Murray S. Kucherawyを連名で掲げる。DKIMを一人の発明として扱う根拠ではない。Authentication-Resultsを定めるRFC 8601はKucherawyを著者とする。IETFの公式profileは2026年9月2日の取得時、RFC一覧に34件を示したが、本人記述は33件のままだった。公式sourceにもfieldとcapture timeが要る。

raw fileの長さからは引けない

l=は.emlのraw byte offsetではない。DKIMはc=で宣言したbody canonicalizationを先に行い、その結果を数える。simpleとrelaxedは空白、改行、末尾の空行を異なる形で処理する。raw body lengthからl=を引くshortcutでは、実際のunsigned tailを誤る。

再現手順は一方向に固定する。raw messageを保存し、複数あるかもしれないDKIM-Signatureから一つをordinal付きで選ぶ。a=、c=、d=、s=、h=、l=、bh=を抽出する。指定algorithmでbodyをcanonicalizeし、長さを測り、l=があればprefixを切り出してbody hashを比較する。その後、観測したpublic keyでheader signatureを検証する。

receiptにはcanonicalized body全体、signed prefix、unsigned suffixの三つのlengthとhashを置く。suffixが正なら、scopeがbody末尾まで届いていないことだけが分かる。誰が何のために加えたかは、hopごとのcaptureまたは宣言されたtransformationとの照合が必要である。

keyにも時点がある。d=はsigning domain、s=はDNS selectorを示す。後日のlookupではrotationやrevocation後の別keyが返る可能性がある。判断時のDNS response、time、resolver、errorを残し、DNSSEC statusは実測した場合だけ記録する。

正常なtrailerと悪意ある追加は同じ余白を使う

l=にはinteroperability上の理由がある。mailing listはunsubscribe情報などをbody末尾に付けることが多い。body全体を署名すれば、その追加だけで元signatureは壊れる。prefixに限定すれば、Verifierは元の部分が維持されたと確認し、追加をlocal policyで扱える。

しかし暗号境界は、善意のlistと攻撃者を区別できない。RFC 6376のsecurity considerationsは、悪意あるintermediaryが自分に有利なcontentを追加できると警告する。MIME structureの変更、寛容なHTML parsing、duplicate detectionの回避によって、末尾がrecipientの画面では元本文を置き換えたように見える場合もある。

したがって、tailを見つけて即座にattackと呼ぶのも、prefixの署名をtailへ広げるのも誤りである。intermediaryを識別し、想定されたfooterやMIME変換と比較し、MIME treeを再構成し、clientが実際に選んだpartを確認する。

Heng Luのrunning-codeの視点では、RFCはbyte処理を定義し、実装のlogが何を計算したかを示す。renderer traceは人が何を見たかを示す。standardの権威もgreen badgeも、その二つのreceiptを代行できない。

h=はheader instanceの順序を要求する

bodyが全てcoveredでも、headerが全てsignedとは限らない。h=はcolon区切りの順序付きlistである。同名fieldが複数あると、DKIMは下側からinstanceを対応させる。nameのsetだけを保存すると、どのSubjectやFromが対象だったか失われる。

Fromは必ず署名対象に含める。RFC 6376はDate、Subject、Reply-To、Sender、MIME fieldsなど表示や処理に影響するfieldの署名を強く勧める。l=使用時のContent-Typeは特に重要で、unsignedな変更により同じprefixが別のcontentとしてrenderされ得る。

header coverage mapはraw headerを順番どおり並べ、選択されたinstanceを示し、表示上重要なのに外にあるfieldを明記する。存在しないfield名をh=で重ねて指定するoversigningも、後の挿入を検出する意味がある。messageに複数signatureがあれば、domain、list、gatewayそれぞれのmapを分ける。

domainの署名はhuman identityの証明ではない

DKIM passは、あるsignatureが、domainとselectorの下で公開されたkeyを使い、選択したmaterialについてverifyできたという結果である。表示上の人物が書いたこと、signing serviceが正しくauthoriseされたこと、本文の真実性、attachmentの安全性、deliveryを証明しない。

RFC 5585は、valid signatureが署名済みmessageまたはportionの非改変を示すと説明し、trustとは分ける。RFC 5863はscope内だけをauthenticとして扱うよう求め、l=を例にする。RFC 8301はalgorithmとkey sizeを更新した。acceptableな強いkeyでも、短いprefixを長くはしない。

DMARCは別の問いを扱う。SPFまたはDKIM identityとvisible From domainのalignmentを調べ、receiver policyのsignalを与える。l=やh=の範囲は変えない。aligned passにもunsigned tailは残り得る。

cryptographic validity、alignment、domain reputation、content safety、delivery policy、rendered viewは別々のcolumnに置くべきである。「authenticated mail」という一語にまとめると、どのcomponentにもないauthorityを作ってしまう。

Authentication-Resultsは挿入者まで確認する

downstream filterはDKIMを再実行せず、上流が書いたAuthentication-Resultsを読むことが多い。RFC 8601はproducerからconsumerへのhandoffを定義するが、field自体に常にintegrity protectionがあるわけではない。

consumerは信頼するauthserv-idを設定し、messageがadministrative boundaryへ入る時、内部で生成されたように装う外部fieldを削除または無効化する必要がある。senderが文字列dkim=passを書ける以上、誰がどの境界内で挿入したかがなければevidenceにならない。

raw field、位置、producer、authserv-id、trust rule、対象signatureを保存する。gatewayが複数signatureを一つのpassへ要約し、d=、s=、h=、l=とのlinkを捨てれば、consumerはscopeを復元できない。

method resultとdispositionも分ける。cryptographyはpassだが部分scopeを理由にrejectするsystemも、warning付きでdeliverするsystemもある。理由を残せば、後のpolicy変更を暗号failureと誤認しない。

stickerではなくscope ledgerを作る

minimum receiptはhashでaddressしたraw messageから始まる。各DKIM-Signatureをoriginal orderで保存し、d=、s=、任意のi=、a=、c=、h=、l=、bh=、signature value、存在する時はsigning/expiry timeを記録する。

次に、観測したDNS keyとtime、reproducibleなcanonicalization、解決したheader instance、full body・prefix・suffixのlengthとhash、verifier software/version、resultとreasonを置く。Authentication-Results producerとtrust boundaryはcryptographic resultの隣に置き、混同しない。

最後にMIME tree、clientが選んだpart、renderer version、l=後のbytesが主要表示に影響したかを残す。privacyを守るため、画面全体を恒久保存せず、part identifierとrender hashでjoinを維持する設計もできる。

Heng Luのagency principleに従えば、standard authorはsyntaxを定義し、key custodianはscopeを選び、intermediaryは変形し、Verifierは計算し、administrative domainはresult channelを保証し、MUAはrenderする。誰も他の全員に代わってmessage全体を保証できない。

最終文はこうなる。何番目のsignatureが、観測したkeyで、順序付きheader instanceとcanonicalized bodyの先頭N octetsを検証した。末尾のこの範囲は対象外だった。信頼済みVerifierが結果を記録し、このrendererがこれらのpartを表示した。human authorship、安全性、意図は別の評価である。

情報源