【ITニュース解説】Writing a TNEF parser so winmail.dat never leaves the browser
2026年10月20日に「Dev.to」が公開したITニュース「Writing a TNEF parser so winmail.dat never leaves the browser」について初心者にもわかりやすく解説しています。
ITニュース概要
Outlookで送られたwinmail.datファイルはTNEF形式のため、他の環境で開けず内容確認が困難だった。この記事は、このファイルをサーバーにアップロードせず、ブラウザ内で安全に解析する技術を解説。4バイトアラインメント、可変長ヘッダ、複数定義のファイル名、圧縮RTF、送信元アドレスの特殊性など、実装で直面した多くの課題と解決策を詳細に紹介している。
ITニュース解説
システムエンジニアを目指す皆さんにとって、日々の業務で「読めないファイル」に遭遇することは少なくないだろう。その代表例の一つが「winmail.dat」という名前のファイルだ。これはMicrosoft Outlookというメールソフトが特定の形式(リッチテキスト形式)でメールを送信する際に生成する特殊な添付ファイルである。通常のメールでは、本文と添付ファイルはそれぞれ標準的な形式で送られるが、Outlookがリッチテキスト形式を使うと、メールの件名、本文の書式、添付ファイル、さらにはOutlook独自の様々な情報(メタデータ)が一つにまとめられ、「TNEF(Transport Neutral Encapsulation Format)」という独自形式のバイナリデータとして「winmail.dat」というファイルに添付されてしまう。この形式はOutlook自身であれば自動的に解釈できるため、送信者は問題に気づかないが、Outlook以外のメールソフトを使っている受信者は、中身が全くわからない「ゴミファイル」としてしか認識できない。
このwinmail.datファイルを開こうとすると、多くの人は対応する変換ツールを探すだろう。しかし、これまでのツールのほとんどは「ファイルをサーバーにアップロードしてください」と求めるものが多かった。winmail.datの中には、契約書や請求書といった個人情報や機密情報が含まれている可能性がある。そのような重要なファイルを、素性の知れない外部のサーバーにアップロードして内容を確認するという方法は、セキュリティやプライバシーの観点から非常にリスクが高い。情報を守るべき順序が逆転していると言える。
この問題に対し、ある開発者が新しい解決策を提案した。それは、TNEF形式を解読する「パーサー」をTypeScriptというプログラミング言語で自ら実装し、その機能をすべてWebブラウザ内で完結させるというものだ。これにより、winmail.datファイルを外部のサーバーに送ることなく、自分のPCのブラウザ上で安全にその中身を確認できるようになる。これは、セキュリティと利便性を両立させた画期的なアプローチと言える。
TNEF形式自体は、一見するとシンプルなデータ構造をしている。ファイルの先頭に署名やキーがあり、その後は「レベル」「属性」「長さ」「ペイロード(実際のデータ)」「チェックサム」といったレコードがファイルの終わりまで連続して並んでいるだけだ。ツリー構造や複雑なインデックス、データを探すためのオフセット(位置情報)などはなく、データの流れを順番に読み進めるだけで良いように見える。実際、この基本的な構造を読み込む部分は比較的短時間で実装できたという。しかし、TNEFの内部に深く入り込むと、様々な「落とし穴」が待ち受けていた。
最初の大きな課題は「4バイトアラインメント」というルールだった。TNEF内の文字列やバイナリデータは、その実際の長さに関わらず、必ず「4バイトの倍数」になるようにデータが詰められていたのだ。例えば、3バイトのデータがあった場合、次の4バイト境界に合わせるために1バイトの「パディング(詰め物)」が追加される。もしこのパディングの処理を忘れてしまうと、データストリームの読み取り位置が1〜3バイトずれてしまい、その後に続く全てのデータが意味不明なものになってしまう。さらに厄介なことに、これはエラーメッセージを出すことなく「サイレントに」発生するため、「ファイルが壊れている」と誤解しやすく、デバッグが非常に困難だったという。パーサーが正しいかどうかを確認するために、正確なバイト数の読み飛ばしが不可欠だった。
次に直面したのが「名前付きプロパティの可変長ヘッダー」という問題だ。特定の属性ID(0x8000以上の値)を持つプロパティは「名前付きプロパティ」と呼ばれ、通常のデータ構造の説明にはない追加のヘッダーが付随していた。しかもこのヘッダーの長さが固定ではなく、プロパティの種類によって変化するという複雑さがあった。もしこの可変長ヘッダーを正しく処理せず、固定長のバイト数を読み飛ばしてしまうと、やはりデータの読み取り位置がずれ、先の4バイトアラインメントの問題と同様に、すべてのデータが意味をなさなくなる。テストファイルではたまたま動いたとしても、実際の様々なwinmail.datファイルでは突然動作しなくなるという、これもまたサイレントな失敗だった。
さらに、添付ファイルの「ファイル名」の扱いは非常に複雑だった。同じ添付ファイルであっても、その名前は最大で4つの異なる場所に格納されており、それぞれが異なる形式や文字コードで保存されていることがあった。例えば、mapiLongFilenameはUnicode(UTF-16LE)でフルネームを保持し、最も正確な情報源となることが多い。一方で、legacyNameはattAttachTitleという属性にあり、添付ファイルのデータに最も近い場所にあるため見つけやすいが、古いWindowsの文字コード(ANSI)で保存され、8.3形式(例: FILENAME.PDF)に切り詰められていることが多かった。特に、日本語のシステムから送られたメールの場合、legacyNameをそのまま解釈すると、文字化けしたファイル名(例: 8E9095948E.PDF)が表示されてしまう。
この文字化けを防ぐためには、「エンコーディングは宣言されている」という重要な事実を理解する必要があった。ANSI文字列がどの文字コードで書かれているかは、0x9007という属性(OEMコードページ)によって明示的に宣言されていたのだ。例えば、932はShift_JIS、65001はUTF-8といった具合だ。この属性は、それが現れた位置から以降のすべてのANSI文字列に適用されるため、パーサーは属性の読み込み順序に厳密に従い、適切なタイミングで文字コードを適用しなければならない。順序を間違えると、日本語の件名が意図しない文字コード(例: Windows-1252)として誤って解釈され、やはり文字化けが発生する。
パーサーが完成した後にも、二つの予期せぬ問題が発見された。一つは「メール本文が全く表示されない」という問題だ。多くのwinmail.datファイルで本文が見つからないと思われたが、実際には本文が存在しており、Outlook独自の「圧縮RTF(PR_RTF_COMPRESSED)」形式で保存されていたのだ。しかも、winmail.datの中ではこの圧縮RTFが本文の唯一のコピーであることが多かった。この圧縮形式はLZ77というデータ圧縮アルゴリズムの一種で、特定の辞書と制御バイトを使って展開する必要がある。展開後のRTFも、OutlookがHTMLメールをRTFに変換する際に独自のタグを付加している場合があり、最終的には不要なRTF部分を削除し、HTML部分のみを抽出する必要があった。抽出されたHTMLは、既知の脆弱性から保護するため、DOMPurifyのようなツールで安全な形式に sanitization(無害化)され、さらにセキュリティポリシーによってネットワークアクセスが制限された隔離された環境(サンドボックス化されたiframe)で表示されることで、リモートの追跡画像などが読み込まれるのを防いでいる。ここでも、RTF内の文字コード(例: \ansicpg932)に従って、バイトの並びを正しくデコードする処理が不可欠だった。
もう一つの問題は「送信者のメールアドレス」だった。winmail.datは送信側の設定で生成されるため、受信側では止めることができない。そこで、このビューアは「送信者にお願いすべきこと」というメッセージと共に、クリックするとメールソフトが開き、送信者のアドレスがToフィールドに自動入力されるmailto:リンクを提供するように設計された。しかし、送信者のメールアドレスを正確に取得するのが想像以上に困難だったのだ。Outlook内部のデータでは、メールアドレスが「X.500パス」という、通常のメールソフトでは送信できない特殊な形式で記録されていることが多かった。インターネット上で通用するSMTPアドレス(user@example.comのような形式)は、PidTagSenderSmtpAddressという別のプロパティに格納されていた。そのため、複数の候補の中から、正規のインターネットアドレスであるものを探し出して使用する必要があった。さらに、代理でメールを送信する場合(アシスタントが上司の代わりに送るなど)には、送信者名と「代表して送っている人」のアドレスが異なってしまうという問題もあり、名前とアドレスが同じ人物のものであることを確認する注意深い処理が求められた。
このように、一見シンプルなファイル形式に見えるwinmail.datの中身を正確に解読し、安全に表示するためのパーサーを実装する過程では、データ構造の隠れたルール、文字コードの複雑さ、圧縮アルゴリズムの解析、そしてセキュリティへの配慮といった、多岐にわたる技術的な課題が横たわっていた。この開発は、システムエンジニアとしてデータと向き合う際に、目に見えない細部まで注意を払い、様々なパターンに対応する必要があることを教えてくれる良い事例と言える。
現在このビューアは、winmail.dat内に添付された別のメールファイルや、OLEオブジェクトの抽出にはまだ対応していないが、ブラウザ内で完結する安全なwinmail.datのビューアとして機能している。もし皆さんがwinmail.datファイルで困った時、このツールは役立つだろう。そして、このようなファイル形式が持つ「四隅」(予測不能な例外)は、一人の開発者の手元にあるファイルだけでは全てをカバーしきれないほど多様であることを示唆している。