boundary string

multipart/* MIME型 boundary 引数

[3] MIME多部分実体 (multipart/* 実体) は複数の実体部分を1つにまとめて扱うことができます。 各実体は境界文字列によって区切ります。 その境界文字列は実体を構成する利用者エージェントが定められた構文的制限内で自由に決定できます。 その境界文字列を指定するのが boundary 引数です。

boundary 引数はすべての multipart/* 実体で必須です。

仕様書

構文

[28] boundary 引数の値は、境界区切子です。

[31] 境界区切子 (boundary delimiter) は、次の文字で構成されます >>26

[39] これらはメール関門に対して頑健である >>26 とされています。

[41] ただし境界区切子の最後は U+0020 ではいけません >>26

[40] 境界区切子は、1文字以上70文字以下でなければなりません >>26

[32] 行頭の --境界区切子に含まれません。

[5] 境界区切子行 (boundary delimiter line) は、 -- から始まり、 boundary 引数の値があり、 省略可能な線形空白があり、 CRLF で終わるです >>26

[29] --RFC 934 との大まかな互換性のためと、 実装方法によっては探しやすいためにあります >>26。 (しかし RFC 934 と完全に互換なわけではありません。)

[30] multipart/* 実体においては境界区切子行の前には CRLF が必要です。この CRLF は境界の一部であり、 前の本体部分前書きの一部ではありません。 >>26

  1. CRLF
  2. --
  3. 境界区切子
  4. *
    1. 線形空白
  5. CRLF

[42] ただし最初の境界区切子行CRLF は、前書きが無い場合、 省略できます >>26

[33] 最後の本体部分の後には閉じ境界区切子行 (close boundary delimiter line) を置きます。これは、境界区切子の後に -- を置いたものです >>26

  1. CRLF
  2. --
  3. 境界区切子
  4. --
  5. *
    1. 線形空白
  6. CRLF

[43] ただし最後の CRLF は、後書きが無い場合、省略できます >>26

[36] 行末の線形空白 (LWSP-char) は、 関門で追加されたものとみなされ、無視されます >>26。 生成してはなりません >>26

[6] ただし、ここに示された文字すべてが境界文字列として適切なわけではありません。 境界文字列は Content-Type: 欄などにおいて boundary 引数の値として指定しますが、その際に引数値は quoted-string を使って指定する方法 (つまり引用符で括る方法) と token を使って指定する方法 (つまり括らない方法) があります。しかし、いろいろな場面での使用を考えると引用符なしの方が安全です。 (例えば multipart/byteranges で引用符付きだと扱えない古い HTTP UA が存在しますRFC 2616。)

そうなると、 token で使用できない文字、すなわち "(" / ")" / "," / "/" / ":" / "=" / "?" / " " は使うべきではないということになります。

特に間隔822 メッセージでは折返しなど種々の問題がありますから、 極力使わないべきです (でも現実には意外とよく使われていたりします (そしてたまに問題を起こします))。コロンも腐った MTAMSA などでの転送時に頭欄名前本体を分けるコロンと誤認されて破壊される問題があることが知られていますから、 使うべきではありません

[827] CGI.pmquoted-string であったとしても ,境界文字列で使うことを認めていません。 これはおそらく、 CGI では複数の同名のヘッダーがあったときに , で連結されることから、 (複数 Content-Type: 欄が存在する要求は非妥当とはいえ) ,境界文字列に現れると解釈が曖昧になるという判断なのでしょう。

[828] と思いましたが同じような構文でそうなっていないところもあるので、 本当の意図はわかりません。

[7] まとめると、境界文字列 boundaryDIGIT / ALPHA / "'" / "+" / "_" / "-" / "." から1文字以上70文字で選ぶのがよさそうだということになります。

[9] なお、構文で最後の文字に間隔を使ってはならないことになっているのは、 古い関門が勝手に 822 メッセージ本体の行末に空白を補うことがあることが知られていたからです RFC 2046 5.1.1

文脈

[27] boundary 引数は、すべての multipart/* MIME型で必須です >>26

処理

[34] 実装は、 (行全体ではなく) 行頭と境界区切子を比較しなければなりません >>26

[35] 境界区切子行だけではなく、境界区切子から始まる本体部分に含めることが禁止されています。

境界文字列の選び方

[8] 仕様上は >>5 の構文に反しない限りで自由に境界文字列を選ぶことができます。 ただし、境界文字列が多部分実体の中に含まれる本体部分の一部として含まれていてはいけません。

[37] 対象データを走査せず、対象データと衝突する可能性が低い境界区切子を選ぶことにしても構いません >>26

[38] MIME に対応していない利用者エージェントの受信者のために可読性が高い境界区切子を選んでも構いません。その場合は衝突により注意が必要です。 >>26

[9] 実際の MIME 利用者エージェントでよく使われている境界文字列は、 無作為に選んだ文字列と日付など規則的に生成した (本体部分に含まれそうにない) 文字列です。また、稀に定型メッセージなどで中身が分かりきっている場合には、 - などの簡単な文字列で済ませることもあります。

どの方法を選ぶにせよ、 生成した境界文字列が本体部分の中身に混じってしまうことがないように注意しなければなりません。 絶対に中身と衝突し得ない文字列を生成する方法はありませんが、 実際に中身を検査すれば含まれているかいないかは簡単に分かりますから、 何度か生成して確認してみれば実用的には十分です (うまい生成方法を選べば普通は1度で衝突しない文字列が得られます)

ただし、 MIME 実体の生成方法や効率上の理由で中身の検査ができないこともあります。 multipart/x-mixed-replace のように、 その性質上あらかじめ絶対に衝突しない境界文字列を選ぶことが不可能なものまであります。 このような場合には、使用する媒体型などの知識に基づき衝突しそうにない文字列を生成するように努力するしかありません。 (本来そのような状況では、 application/vnd.pwg-multiplexed のような別の方法を採用するべきです。)

[13] なお、 multipart/* は何重にも入れ子にできます。 当然、内側と外側では別の境界文字列を選ばなければなりません。 日付などに基づき生成する方法を選んだ場合は同時に同じ境界文字列を生成してしまわないように注意しなければなりません。

多部分実体の境界文字列

[10] 多部分実体は次のような構文と規定されています RFC 2046 5.1.1

(詳細は multipart/* の項や RFC 2046 を見てください。)

[11] 境界文字列に関係する部分をまとめると、次のようになります。

ただし、 boundary というのは boundary 引数で指定した文字列です。 最後の境界文字列は boundary の後にも -- が付くことと、最初の境界文字列以外では --boundary の直前の CRLF も境界の一部である (本体部分の一部ではない) ことに注意してください。

[12] ただし、 >>9 のような腐った関門が勝手に空白を追加しても受信側が理解できるように、 受信した側の MIME 利用者エージェントは --boundary--boundary-- の後に空白があっても無視することになっています (構文の transport-padding)。 (だからといって、送信側が意図的に空白をつけることは禁止されています)

[17] なお、古い版の MIME では一番最初の本体部分の前の境界でも --boundary の前の CRLF が必須とされていましたが、今日では不具合であったと考えられています。

multipart/form-data 境界文字列

[824] HTMLフォーム提出算法において、 multipart/form-data 境界文字列 (boundary string) とは、 multipart/form-data実体本体を生成するに当たり使われた boundary の文字列のことをいいます。

[826] multipart/form-data境界文字列は、 Content-Type: 欄の値の生成に使われます。

本体部分における制約

[25] 本体部分は、境界区切りと一致する、または境界区切りで始まるを含んではなりません >>4

歴史

MIME 以前

[1] RFC 934電子メール内にメッセージをカプセル化して埋め込む方法を規定していました。 RFC 934 は、 - から始まるencapsulation boundary (EB) と呼び、メッセージ本文中の EB をどう処理するかを定義していました。

MIME とは違って何が EB であるかをメタデータとして保持しているわけではないので、 MUA が本文を走査して解釈しなければなりません。
詳しくは RFC 934 を参照。
[18] この方法はそれ以前からの慣習を規定したものである >>2 ようです。

[21] PEM は暗号化や署名が施されたメッセージRFC 822 メッセージに埋め込む形を採っていました。 「内側」のメッセージの前後には、 RFC 934 に添って -----PRIVACY-ENHANCED MESSAGE BOUNDARY----- という行を含めることになっていました >>2

[22] 両仕様は埋め込まれたメッセージの前にある EBpre-EB、 後にある EBpost-EB と呼んでいました。

[24] PEM の第4世代では、 pre-EB-----BEGIN PRIVACY-ENHANCED MESSAGE-----post-EB-----END PRIVACY-ENHANCED MESSAGE----- になぜか変更されています >>23

MIME

[14] MIMEContent-Type: 欄で boundary 引数を指定する例 RFC 2046:

Content-Type: multipart/mixed; boundary=gc0p4Jq0M2Yt08j34c0p

[15] 間違った例 RFC 2046:

Content-Type: multipart/mixed; boundary=gc0pJq0M:08jU534c0p

boundary 引数の値に : が含まれていますが、この文字は tspecials に含まれているので、 quoted-string にしなければ (値全体を二重引用符で括らなければ) なりません。

[16] multipart/alternative を使った電子メイルの完全なメッセージの例 RFC 2046, 改:

From: Nathaniel Borenstein <nsb@bellcore.com>
To: Ned Freed <ned@innosoft.com>
Date: Mon, 22 Mar 1993 09:41:09 -0800 (PST)
Subject: Formatted text mail
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary=boundary42
--boundary42
Content-Type: text/plain; charset=us-ascii
  ... plain text version of message goes here ...
--boundary42
Content-Type: text/enriched; charset=us-ascii
  ... RFC 1896 text/enriched version of same message goes here ...
--boundary42
Content-Type: application/x-whatever
  ... fanciest version of same message goes here ...
--boundary42--

メモ

[19] [mew-dist 27367] Re: < > を含むバウンダリでエラー (2007-02-07 15:22:41 +09:00 版) <http://www.mew.org/pipermail/mew-dist/2006-October/027053.html>

[20] [mew-dist 27666] MIME decoding error: No boundary parameter for multipart (pegacorn 著, 版) <http://permalink.gmane.org/gmane.mail.mew.general.japanese/6020>

[823] Apache HTTP Server Project ( ( 版)) <http://httpd.apache.org/docs/1.3/misc/known_client_problems.html#boundary-string>

[829] 境界文字列はその複数部分実体内に含まれる実体に含まれるものであってはなりません。

[830] >>829 でもこの(必然的な)規制って、 multipart/x-mixed-replace のように複数部分が動的に生成される場合であって Content-Type: 欄を生成する時点で完全に内容を把握できない場合に満たすことを保証するのが難しい気がします。

[831] >>830 そうでなくても、常に binary CTE である HTTP での転送の時なんかに、内容と一致しない境界文字列を生成出来ない可能性がある気がします。確率はとっても低くて無視できるかもしれませんが...

[832] RFC Errata Report ( ( 版)) <http://www.rfc-editor.org/errata_search.php?rfc=2388&eid=4030>