世界を動かす技術を、日本語で。

等号は一体どうなっているの?

概要

  • Twitterで古いメールの抜粋が多く投稿され、 イコール記号 の謎が話題
  • イコール記号は quoted-printableエンコーディング によるもの
  • 行継続や特殊文字の エンコード処理の不備 が原因
  • メール処理時の デコーダーのバグや不適切な変換 が混乱を招く
  • まとめ: 技術的背景処理ミス が原因

Twitterで話題の古いメールに現れるイコール記号の正体

  • 最近、Twitterで 古いメールの抜粋 が多く投稿されている現象
  • 多くの人が疑問に思うのが イコール記号(=)の多用 の理由
  • 一部では 暗号説やOCR由来説 が出ているが、どちらも誤り
  • 原因はメールを 可読形式に変換した際のミス によるもの
  • メールは単なるテキストではなく、 エンコード が施される仕様

quoted-printableエンコーディングとは

  • 80年代まではメールは 単純なテキスト 形式
  • 長い行や特殊文字(“rock döts”など)対応のため エンコード方式 が導入
  • 代表的なのが quoted-printable 方式
    • 長い行を分割する際、 行末に=記号を挿入 し、次行と連結
    • 例:「non- cloven hoofs」を「non- =CRLF cloven hoofs」と分割
  • 表示時は「=CRLF」を 全て削除 して1行に復元

イコール記号が残る理由とデコードの失敗

  • メールサーバーは通常 CRLF(Windows形式) の改行を使用
  • 変換時に NL(Unix形式) へ変換されることが多い
  • 不適切なデコードアルゴリズムの場合
    • 「行末の=記号を見つけて2文字+イコールを削除」→ 1文字消失バグ
    • 例:「non- =NL cloven hoofs」が「non- loven hoofs」になる
  • さらに、 イコール記号が消えずに残るケース も発生

quoted-printableでの特殊文字エンコード

  • イコール記号は 長い行の継続 以外にも用途あり
  • 特殊文字のエンコード(例: =C2=A0はノンブレークスペース
  • 不適切なデコード処理で「=C2」や「=A0」が そのまま残る現象
  • 例:「=C2 please note」や「=A0」でインデントを表現

まとめと考察

  • イコール記号が多発する理由
    • 技術的なエンコード処理
    • 行継続や非ASCII文字デコードのバグ
    • メール処理担当者の知識不足や不適切な変換
  • quoted-printableは本来 受信時にデコードされるべき仕様
  • しかし実際は “生”のまま残るケース が多発
  • Windows(CRLF)Unix(NL) の改行コードの違いも一因
  • SMTPサーバー向けのアルゴリズムを流用したことによる 互換性問題

エンジニア・運用者向けの注意点

  • メールの quoted-printableデコード は正規の実装が必須
  • 改行コードの違い を十分理解した運用設計
  • 特殊文字やエンコード形式 の正確な取り扱い
  • 表示や変換処理時の バグ検証 の徹底

Hackerたちの意見

https://web.archive.org/web/20260203094902/https://lars.inge... このサイト、HNの死のキスを受けたのかな?

CLRFとLFの問題、また出てきたね。なんでそもそも最大行長の制限があるんだろう?技術的な理由なのか、それとも表示に関することなのか気になる。

サーバーがユーザーの入力に文字を挿入するのがいいアイデアだと思ってるのか、ちょっと疑問だわ。もし同僚がこれを提案したら、笑っちゃうよ。なんかすごくハッキーで、リアルな解決策だとは思えない。

提出されたもの以外では見たことないけど、長さが合ってれば、生のメールから処理された可能性があるね。RFCではラップする長さが定義されてるし。編集: そうだね、たぶんそれが一番の理由だと思う(78文字以内にするべき、998文字以内にする必要がある)。CRLFの使用についても指定されてるのを忘れてた。ここで説明されてるように、Windowsとは関係ないよ。これが私の「notmuch-more」メールライブラリにあるやつ: https://github.com/OJFord/amail/blob/8904c91de6dfb5cba2b279f...

じゃあ、ここで何が起こったの?最初にこれらのメールを集めた人は、CRLF(つまり「Windows」の行末コーディング)から「NL」(つまり「Unix」の行末コーディング)に変換したんだ。メールを扱うなら、これはかなり普通のことだよ。でも、そうするとバイトが一つ減る。もう一つの可能性として、歴史的に変換が行われたかもしれない。みんなこれらのメールがGmailからの正確なダンプだと思ってるけど、エプスタインがGmailから第三者のメールサーバーにメールを同期してた可能性もあるんじゃない?Stackoverflowの投稿が2011年の正確な状況を詳しく説明してるから、直接Gmailからではなく、二次的なメールサーバーから収集されたデータを見ている可能性も考慮すべきだと思う。これを否定するものはあるかな?(間違ってなければ、Quoted-Printableエンコーディングを二回適用することで「=」の問題も見えると思うし、行末の扱いだけではなく、二つのメールサーバーが関係してると思う。これが「=」記号が保持される理由にもなるね。)

これが一番の理由っぽいね!

かなり長い行だね。メールサーバーはそれが嫌みたい。なんでメールサーバーは行の長さを気にするんだろう?クライアントがメールを読むときに行を折り返すのを心配すればいいのに。

一番シンプルな理由: メールサーバーは長い間、メールクライアントにテキストコンテンツの部分文字列を送る機能を持ってたんだ。全体を転送することなくね。Gmailの受信トレイの表示みたいに、メッセージを開く前にね。Quoted Printableは、テキストやHTML(人間が読めるメール本文)などのMIMEタイプに対してだけ有用なエンコーディングだったと思う。バイナリ(例えば、添付ファイル、画像、動画)には関係ない。メールサーバーは(望めば)バイナリタイプを不透明な塊として扱えるけど、テキストタイプはクライアントへのメッセージリストの効率的な転送のために読まれることができるんだ。

僕の記憶が正しければ、90年代にこの手のことが始まった頃、ほとんどのメールサーバーは結構まともだったんだよね。でも、どこかのサーバーが古いIBMのハードウェアでEBCDICエンコーディングを使っていて、パンチカードに基づいて72文字に切り詰めるんじゃないかっていう、ある意味での不安が常にあった。だから、そういう奇妙なシステムに対応するための標準が作られたんだ。HNには実際にそんなサーバーを使ったことがある人がいると思うよ。

昔のことを考えると、メールは世界的なコミュニケーション手段じゃなかったんだよね。むしろ、組織内だけで使うメールシステムが一般的で、どんなレガシーのメインフレームを使っていても、その制約の中で動いていた。90年代にインターネットが広がり、外部組織へのメールが増えてきたとき、古い端末ベースのメールプログラムとの互換性も気にしなきゃいけなかったから、システム設計の選択肢が変わってきたんだ。

80年代から90年代にかけて、実装を簡単にするために静的バッファを使うのが一般的だった。固定サイズのバッファを割り当てて、バッファサイズを超える行のメッセージは拒否するって感じ。SMTPのRFCでは1000文字制限(\r\nを含む)を指定してるけど、87文字で折り返すのが一般的で、これだと小さい画面でソースを確認しやすいんだよね。

メールは行ごとに処理されていて、通常は固定長のバッファを使ってた。これで動的メモリ割り当てを避けて、ストリーミングパーサーを書く必要がなくなる。RFC 821では行の長さを最大1000バイトに制限したけど、80文字以下で折り返す仕組みがあれば、古いメールソフトとの互換性が高まるし、ターミナルで生のメールを表示する時にも便利なんだ。だから、MIMEのBase64も通常は76文字ごとに改行を入れるんだよね。

RFC822は、シンプルな表示ソフトを持つシステムでの可読性のためのものだって明言してる。1982年のプロトコルで、その頃のシステムは4〜16KBのRAMしかなかったから、当時の薄型クライアントシステムに何か前処理されたものを提供するのは意味があったんだろうね。

SMTPは行ベースのプロトコルで、メッセージボディを転送する部分も含まれてる。サーバーはメッセージヘッダーを解析する必要があるから、オペークな塊にはできないんだ。クライアントがIMAPを使う場合、サーバーはメッセージを完全に解析する必要がある。唯一の代替手段はPOP3で、クライアントがすべてのメッセージを塊としてダウンロードして、一つの場所からしかメールを読めない。2000年にはそれが理にかなってたけど、今はみんな複数のデバイスを持ってるから、通用しないよね。

これがSMTPでのメールの仕組みだよ。各コマンドを送ると、'200'クラスのメッセージ(成功)か、400/500クラスのメッセージ(失敗)が返ってきた。聞き覚えある? telnet smtp.mailserver.com 25 HELO MAIL FROM: me@foo.com RCPT TO: you@bar.com DATA blah blah blah 調子はどう?また後で話そう! . QUIT

記事は、=、==、===、.=、>、(==)、=>、=~などの演算子のさまざまな意味についてだと思ってた。

これって、アリ用のHaskellなの?

本当のオチは、これは「危険なほどの知識しかない」っていう完璧な例だってこと。これらのメールを処理した人は、メールがプレーンテキストじゃないことは知ってたけど、quoted-printableデコーディングを手作業でやるものじゃないってことは知らなかった。HTMLを正規表現で手動でパースするのと同じバグのクラスで、うまくいくけど、いきなりダメになることもあって、議会の証拠が謎のイコールサインでいっぱいになるんだよね。

今、トップの人たちがそれに取り組んでるよ。

「これはHTMLを正規表現で手動で解析するのと同じクラスのバグだね。うまくいくけど、いつかはダメになるっていう。これについてはもう知ってると思うけど、他の人のために、僕のお気に入りのStackOverflowの回答をシェアするよ。」 https://stackoverflow.com/a/1732454

この記事からの私の主なポイントは、改良された蹄がない豚たちに何が起こったのか知りたいってことだね。

自分でメールアーカイブソフトを作ったんだけど、一番大変だったのは20年以上の.emlファイルの中の変なケースに対処することだった。概念的にはシンプルなのに、メールって意外と複雑なんだよね。

なんで今になってこの問題が出てきたのか気になる。なんで急にみんな古いメールを欠陥のあるQPデコーダーで投稿し始めたの? > 「何かの理由で、ここ数日間、Twitterで古いメールの抜粋を投稿する人が多くなってる。」最新のミームやSNSのドラマを見逃してるかもしれないけど、この「何かの理由」って何なのか知ってる人いる? 編集: 質問に答えてもらった。

おそらくエプスタインのファイルだと思うけど、Twitterやってないから確かじゃない。

DOJがエプスタインのメールをまた一 bunch 公開したね。

それは明らかにジョークだね。過去3日間にエプスタインのファイルから投稿されたメールに気づいてないふりをしてる。

アーカイブのこの記事がまさにこの問題を抱えてるのが面白いね。 https://pastes.io/correspond https://news.ycombinator.com/item?id=46843805