HEARTBEATS

これでメールは怖くない!メールソースの見方

   

こんにちは。技術開発室の伊藤です。今回はメールの基本的な構造やヘッダーフィールドについてまとめてみました。また、文字エンコーディングや、複数のコンテンツを扱うためのMIME(Multipurpose Internet Mail Extensions)についても紹介します。

メールの構造

 メールの構造はinternet message formatとして『RFC 5322 Internet Message Format』にて定義されています。RFC5322ではメールの文章のことをメッセージと表現しているため、以降このブログでもメッセージと表現します。 メッセージはヘッダーセクションとボディに分かれています。ヘッダーセクションとはヘッダーフィールドが記述されているエリアで、ボディとは本文のことです。ヘッダーセクションの後に空行(改行のみの行)を挟んでボディが続きます。

 ヘッダーフィールドとボディそれぞれ各行の文字数は998文字以内にしなければいけません。 加えて、昔のテキスト端末では画面の幅の制約で80桁までしか表示できなかった都合上、ヘッダーフィールドとボディそれぞれ各行の文字数は78文字以内にすることを推奨しています。ただし、現在のメーラーではそんなことは無いため、メール作成時に気にする必要はありません。『RFC 3676 The Text/Plain Format and DelSp Parameters』にしたがってソース上では78文字以内に調整してくれます。

 なお、メッセージに利用できるのはUS-ASCIIの文字エンコーディングです。

例: メールソース

MIME-Version: 1.0
Date: Thu, 10 Nov 2022 18:04:33 +0900
Message-ID: 
Subject: test Subject
From: foo 
To: test@example.com
Content-Type: text/plain; charset="UTF-8"

this is body

ヘッダーフィールド

 ヘッダーフィールドは"フィールド名: フィールド本体"の形で表現されます。ヘッダーフィールドの末尾には必ずCRLF(キャリッジリターン + ラインフィード)の制御文字を挿入する必要があります。 長いヘッダーフィールドの場合にはCRLFで改行した後、ホワイトスペース(タブや空白)から始めることで複数行にできます。これをfolding(折りたたみ)と呼びます。下記の2つのSubjectヘッダーはどちらも同じです。

一行にした場合

Subject: This is a test

複数行にした場合

Subject: This
 is a test

 RFC 5322で定められている必須のヘッダーフィールドは、実はDateヘッダーフィールドとFromヘッダーフィールドの2つしかありません。RFC 5322以外にも色々なRFCでヘッダーフィールドが追加されていますが、ここではRFC 5322で定義されたヘッダーフィールドを紹介します。

RFC5322 - 3.6.1. The Origination Date Field

 時刻に関するヘッダーフィールドです。

Date

 作成者がメールを作成した日時を示します。Dateヘッダーフィールドは必須になっています。 日付の形式は決まっており、「[Day of week], Day Month Year Hour:Minute[:Sec] TimeZone」の形式になります。曜日と秒だけは省略が可能です。

例: Date: Mon, 15 May 2023 00:00:00 +0900

RFC5322 - 3.6.2. Originator Fields

 送信元の情報に関するヘッダーフィールドです。始めにメールアドレスの表現の仕方について簡単に説明します。

 メールアドレスは"表示名 <実際のメールアドレス>"という形で表現されます。表示名は省略でき、そのときにはメールアドレスを"<>"で囲う必要はありません。また、","で区切ることにより複数のメールアドレスを指定できます。

例: To: foo <foo@example.com>,bar@example.com

From

 メッセージ作成者のメールアドレスを示します。

例: From: foo <foo@example.com>

Sender

 実際にメッセージを送信する代理人を示します。メッセージの送信者が作成者と異なるときに使用されます。

例: Sender: foo <foo@example.com>

Reply-To

 返信先のメールアドレスを示します。Fromとは異なる返信先を指定するときに使われます。メールアドレスは複数指定可能です。

例: Reply-To: foo <foo@example.com>

RFC5322 - 3.6.3. Destination Address Fields

 送信先の情報に関するヘッダーフィールドです。

To

 送信先のメールアドレスを示します。メールアドレスは複数指定可能です。

例: To: foo <foo@example.com>

Cc

 Ccヘッダーフィールドに指定したメールアドレスにはこのメールのコピーを送信します。ちなみにCcはカーボンコピーの略です。メールアドレスは複数指定可能です。

例: Cc: foo <foo@example.com>

Bcc

 Bccヘッダーフィールドもコピーが送信されますが、Bccに指定したメールはBccヘッダーフィールドが削除された状態で送信されます。そのため、コピーしたメールが他に誰へ送信したのかわからないようになります。Bccはブラインドカーボンコピーの略です。メールアドレスは複数指定可能です。

例: Bcc: foo <foo@example.com>

RFC5322 - 3.6.4. Identification Fields

 メッセージを識別するための情報に関するヘッダーフィールドです。

Message-ID

 メッセージを識別するためのヘッダーフィールドです。Message-IDはグローバルで重複してはいけません。

例: Message-ID: <0000000000000000@example.com>

In-Reply-To

 返信したメールのMessage-IDを示すためのヘッダーフィールドです。

例: In-Reply-To: <0000000000000000@example.com>

References

 Referncesヘッダーフィールドは主にメールのスレッド表示に利用されます。返信した元のメールのReferencesヘッダーフィールドに加え、元のメッセージのMessage-IDなどが入ります。

例: References: <0000000000000000@example.com>

RFC5322 - 3.6.5. Informational Fields

 人がメールを読むために必要な情報のヘッダーフィールドです。

Subject

 メールの件名を示します。

例: Subject: test Subject

Comments

 コメントを示します。ほとんどのメーラーがこのフィールドを扱わないため、実際にはほとんど使われていません。

例: Comments: test comment

Keywords

 本文に関するキーワードを示します。こちらもほとんどのメーラーがこのフィールドを扱わないため、実際にはほとんど使われていません。

例: Keywords: phrase1,phrase2

RFC5322 - 3.6.6. Resent Fields

 転送に関する情報のヘッダーフィールドです。転送時にはそれぞれオリジナルのヘッダーフィールドの上にResent-から始まるヘッダーフィールドに転送時の情報を追加します。

  • Resent-Date: Mon, 15 May 2023 00:00:00 +0900
  • Resent-From: foo <foo@example.com>
  • Resent-Sender: foo <foo@example.com>
  • Resent-To: "foo" <foo@example.com>
  • Resent-Cc: foo <foo@example.com>
  • Resent-Bcc: foo <foo@example.com>
  • Resent-Message-ID: <0000000000000000@example.com>

RFC 5322 - 3.6.7. Trace Fields

 メールの送信時の情報に関するヘッダーフィールドです。トレースヘッダーフィールドは順番を入れ替えてはいけません。

Return-Path

 Return-PathヘッダーフィールドはメッセージがSMTP環境から離れて最後の配送をする際につけられるヘッダーフィールドです。SMTPプロトコルの通信時に"MAIL FROM"コマンドで指定されるメールアドレス(エンベロープFrom)が指定されます。

例: Return-Path: <test+bounce@example.com>

Received

 Receivedヘッダーフィールドはメールを送信するために経由したメールサーバーの情報を確認できます。

例: Received: from example.com by x.y.test; Mon, 15 May 2023 00:00:00 +0900

 トレースヘッダーフィールドの順番は変えてはいけないこと、また上に追記していくという制約があるため、下からReceivedヘッダーフィールドを追いかけることでメールサーバーの経路を確認できます。下記の例ではメール送信までに2つのメールサーバーを経由していることがわかります。

Received: from mail.example.com. (mail.example.com. [198.51.100.100]) 
   (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
     key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by mail.example.com (Postfix) with ESMTPS id 827CA105F1C1 for ; Sun, 14 May 2023 17:01:57 +0900 (JST)
Received: from [192.0.2.11] (localhost [127.0.0.1]) by mail2.example.com (Postfix) with ESMTP id 6F6A51004FE72 for ; Sun, 14 May 2023 17:01:57 +0900 (JST)

RFC5322 - 3.6.8. Optional Fields

 定められたヘッダーフィールド以外にも自分で独自のヘッダーフィールドを付与できます。X-から始まるヘッダーフィールドなどをよく見かけます。なお、X-から始まるヘッダーフィールドについての議論は『RFC 6648 Deprecating the "X-" Prefix and Similar Constructs in Application Protocols』というRFCを参照してください。

ボディ

 ヘッダーフィールドのあとに空行(改行のみの行)で区切ってボディを記述します。ボディではCR(キャリッジリターン)とLF(ラインフィード)はCRLFとして合わせた状態で利用しなければいけません。 多くの場合、後述するMIME(Multipurpose Internet Mail Extensions)という仕組みを合わせて使います。

MIME(Multipurpose Internet Mail Extensions)

 もともとはメールでテキストデータだけがやり取りされていましたが、時代が進むにつれて画像や音声などの7ビット符号以外のデータやUS-ASCII以外の文字エンコーディングを利用するための仕組みが必要になりました。それを表現するための拡張としてMIME(Multipurpose Internet Mail Extensions)という仕組みがあります。MIMEはいくつかのRFCに分けられて定義されています。

その中で本文に関するMIMEの仕様は『RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies』で定義されています。 RFC 2045で追加されるヘッダーフィールドは下記の3つです。

MIME-Version

 MIMEのバージョンを指定します。現在までに定義されているバージョンは1.0のみです。

例: MIME-Version: 1.0

Content-Type

 Content-Typeヘッダーフィールドはコンテンツの内容がどんな形式のものかを示すために利用されます。下記のような形式になります。

Content-Type: タイプ/サブタイプ; パラメーター

タイプ/サブタイプや、パラメーターとして指定できるものは 『RFC 2046 Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types - 3. Overview Of The Initial Top-Level Media Types』にて定義されています。なおタイプ/サブタイプをまとめてメディアタイプと呼びます。

メディアタイプはIANA※3(Internet Assigned Numbers Authority)で管理が行われています。一覧はIANAのMedia Typesから確認できます。

後述するmuiltpartの仕組みで説明しますが、Content-TypeヘッダーフィールドとContent-Transfer-Encodingヘッダーフィールドはヘッダーセクションだけではなくボディの中にも指定されることがあります。ヘッダーセクションに記載される場合にはボディ全体のコンテンツに関する情報になります。ボディの中で指定される場合にはその区切りまでの内容を示します。

※3 IANAとはインターネットで利用される様々なプロトコルやそこで利用される値を管理している組織です。

Content-Transfer-Encoding

 エンコードの方法を指定します。

例: Content-Transfer-Encoding: base64

利用できるのは下記の5種類です。

種類 説明
7bit 7ビット符号のテキストであることを示します。デフォルトではこれが指定されます。
8bit 8BITMIME※1という仕様を利用した場合に利用できます。8ビット符号のテキストであることを示します。
binary BINARYMIME※2という仕様を利用した場合に利用できます。バイナリ化されたデータであることを示します。
quoted-printable Quoted-Printableエンコーディングによりエンコードします。
base64 Base64エンコーディングによりエンコードします。

※1 8BITMIMEとは『RFC 6152 SMTP Service Extension for 8-bit MIME Transport』にて定義されているSMTP拡張の1つです。SMTP通信時に8bitのメッセージをそのまま送信できるようになる拡張です。

※2 BINARYMINEとは『RFC 3030 SMTP Service Extensions for Transmission of Large and Binary MIME Messages』で定義されているSMTPプロトコルの拡張で、"DATA"コマンドのかわりに"BDAT"コマンドを利用してバイナリを送信できる仕組みです。ただし、ほとんどのMTAでは対応していないため実際には使われていません。

Quoted-PrintableとBase64のエンコード方法について簡単に説明します。

Quoted-Printable

 Quoted-Printableエンコーディングとは『RFC 2045 - 6.7. Quoted-Printable Content-Transfer-Encoding』で定義されているエンコーディングで、データを"="+"16進数2桁"の表示に直す方法です。例えばUTF-8でひらがなの"あ"は16進数で表現すると"E38182" なので"=E3=81=82"となります。

Base64

 Base64エンコーディングとは『RFC 2045 - 6.8. Base64 Content-Transfer-Encoding』で定義されているエンコーディングで、下記の手順でエンコードを行う方法です。

  1. 文字列をすべて2進数にする
  2. 6bitずつ区切る(0でパディング)
  3. 10進数に変換する
  4. 変換表※2に従い変換する
  5. 4文字区切りにする(=でパディング)

※2 『RFC 2045 - 6.8. Base64 Content-Transfer-Encoding 内 Table 1: The Base64 Alphabet』

UTF-8の"あ"を実際にエンコードしてみます。 "あ"は16進数で表現すると"E38182"なので、2進数で表すと"11100011 10000001 10000010"となります。

6bitずつ区切ると"111000 111000 000110 000010"となり、それぞれを10進数に変換して"56 56 6 2"と表すことができます。変換表に従って変換すると"44GC"となります。

Base64エンコーディングはURLなどでもbase64urlというエンコード方式として利用されます。base64との違いは変換表にある"/""+"の扱いです。"/""+"はURLで使ってしまうと別な意味を持ってしまうため、base64urlの変換表ではそれぞれ"-""_"に変更されています。詳細は『RFC 4648 The Base16, Base32, and Base64 Data Encodings - 5. Base 64 Encoding with URL and Filename Safe Alphabet』を参照してください。

また、Base64エンコードは基本的にエンコード前に比べて33%程度データ量が多くなるという特徴があります。

複数のコンテンツを表現するための仕組み(multipart)

 画像や映像、テキストなど複数のコンテンツをメッセージ内で表現するためのタイプとして"multipart"が用意されています。サブタイプが"mixed"の場合はメッセージと添付ファイルなど、"alternative"の場合は同じ内容の文章を複数の表現方法で表したいとき(例:テキストとHTMLメール)に指定します。

"multipart"をタイプに指定した場合はパラメーターとして"boundary=文字列"を指定します。 境界線は"--"+boundaryで指定した文字列になります。境界線の下にContent-TypeヘッダーとContent-Transfer-Encodingヘッダーを挿入し、境界線で区切られた区間ごとでボディパートとしてコンテンツを表示します。また、最後の境界線はさらに末尾に"--"が追加されます。

例えば、テキストとHTMLで同様の内容を表示するメールは下記のような構造になります。

メールにUS-ASCII以外の文字エンコーディングを利用する

 メールはUS-ASCIIという文字エンコーディングを利用しなければいけません。それ以外の文字エンコーディングを利用したい場合にはMIMEの仕組みを利用する必要があります。

Content-Typeヘッダーフィールドのパラメーターに"charset=文字エンコーディング"として利用したい文字エンコーディングを指定することで、US-ASCII以外の文字エンコーディングを利用できるようになります。 利用できる文字エンコーディングもIANAで管理が行われています。一覧はIANAのcharacter-setsから確認ができます。日本語の場合は"ISO-2022-JP""UTF-8"が指定されます。指定がない場合は下記例のContents-Typeヘッダーフィールドが指定されているものとして処理されます。

例: Content-type: text/plain; charset=us-ascii

ヘッダーフィールドでもUS-ASCII以外の文字エンコーディングを使う

 ボディだけでなくヘッダーフィールドにもUS-ASCII以外の文字エンコーディングを利用したいことがあります。ヘッダーフィールドにMIMEを適用するための仕組みが『RFC 2047 MIME (Multipurpose Internet Mail Extensions) Part Three: Message Header Extensions for Non-ASCII Text』にて定義されています。下記のように表現されます。

"=?" charset "?" encoding "?" encoded-text "?="

charsetはIANAに登録された文字エンコーディングを指定します。encodingはQまたはBのどちらかを指定します。QはQ encodingとして『RFC 2047 - 4.2. The "Q" encoding』で定義されており、ボディで使われるQuoted-Printableとは似ていますが別なものです。BはB encodingとして『RFC 2047 - 4.1. The "B" encoding』で定義されており、ボディで使われるBase64エンコーディングと同じです。

なお、このヘッダーが利用できる場所にも制約があります。メールアドレス("<>"内の実際のアドレス)やReceivedヘッダーフィールド、MIMEに利用されるヘッダーフィールドには利用できません。また、引用内のなかにも利用できません。長さにも制限がありフィールド本体の長さは76文字以内にする必要があります。

例えば、件名で「これはtestメールです」をUTF-8の文字エンコーディングで、エンコード方式をB encodingにした場合は下記のようになります。

=?UTF-8?B?44GT44KM44GvdGVzdOODoeODvOODq+OBp+OBmQ==?=

日本語メールの件名や本文が文字化けしてしまっている場合にはこのあたりのヘッダーフィールドが正しく設定されているかどうかを確認しましょう。

まとめ

 メールの基本的な構造とヘッダーフィールド、そしてMIMEについて簡単に紹介しました。これでメールのソースがちょっとだけ読めるようになるはずです。

参考文献

株式会社ハートビーツの技術情報やイベント情報などをお届けする公式ブログです。



ハートビーツをフォロー

  • Twitter:HEARTBEATS
  • Facebook:HEARTBEATS
  • HATENA:HEARTBEATS
  • RSS:HEARTBEATS

殿堂入り記事