([[名無しさん]])
[66]
あ
い
う
え
お
(326 [あああ] [WEAK[2005-07-27 09:32:06 +00:00]])
[[#comment]]
** 欄名 (制御子名)
[28] 各欄は名前を持ちます。名前はフォーム内で固有です。
[SRC[RFC 2388 3.]] 欄名が同じ本体部分が複数あるときの取扱いは
RFC 2388 では規定されていません。 [SRC[RFC 2388 5.]]
応用により、例えば HTML では[[フォーム・データ集合]]に同じ名前の制御子名があれば、
複数の本体部分が同じ名前となることがあります。
各本体部分は、 [CODE(MIME)[[[Content-Disposition]]]]
を [CODE(MIME)[[[form-data]]]] とし、その [CODE(MIME)[[[name]]]]
引数に対応する制御子の欄名 ([[制御子名]]) を指定します
[SRC[HTML 4 17.13.4.2, RFC 2388 3.]]。
[29] 非 [[ASCII]] 文字を含むときには、
[[RFC 2045]] で説明されている方法で符号化して構いません
[SRC[HTML 4 17.13.4.2]]。と書いてはあるのですが、
[Q[構いません]]ではなくて何らかの方法で符号化しなければ'''なりません'''
(MIME 頭欄は ASCII と定義されています)。さて、 RFC 2045
の一体どこで [CODE(MIME)[Content-Disposition]] [CODE(MIME)[name]]
を符号化する方法が説明されているのでしょうか? されていません。
詳しくは [CODE(MIME)[[[name]]]] 引数の説明をご覧ください。
相当する部分は、 RFC 2388 では [[RFC 2047]] を参照しています
[SRC[RFC 2388 3., 5.4]]。 RFC 2047 と言うからには [CODE(ABNF)[[[encoded-word]]]]
を使うのでしょう。普通 [CODE(ABNF)[[[quoted-string]]]] でが
[CODE(ABNF)[encoded-word]] は使わない (使えない) ものですが、
明示的に 2047 を参照しているのですから [CODE(MIME)[name]]
引数では特別に使えるのでしょう。
[30]
例 [SRC[HTML 4 17.13.4.2]]
[PRE(MIME)[
Content-Disposition: form-data; name="mycontrol"
]PRE]
この例では、制御子名 [SAMP[mycontrol]] を表します。
この欄を含む本体部分の[[本体]]は、この名前の制御子の[[現在値]]
(またはファイル内容) になります。
[[#comment]]
** 本体
[31]
各本体部分の[[本体]]は、ファイル選択制御子
([[[CODE(HTMLe)[input]]//[CODE(HTML)[file]]]]) ではファイルの内容、
それ以外では[[現在値]]になります。
[WEAK[(という説明が HTML 4 仕様書ではきちんとなされていません。)]]
[[#comment]]
** ファイルの場合
[32] 本体にファイルの内容を入れる場合には、適当な[[媒体型]]か、
分からなければ [CODE(MIME)[[[application/octet-stream]]]]
を指定するべきです。
[SRC[HTML 4 17.13.4.2, RFC 2388 3., RFC 2388 4.2]]
[[#comment]]
*** 複数ファイルの同時提出
[51] 1つのフォーム項目として複数のファイルを同時に提出する場合には、
[CODE(MIME)[[[multipart/mixed]]]] を使って1つの[[本体部分]]とします。
[SRC[HTML 4 17.13.4.2, RFC 2388 3., RFC 2388 4.2]]
ファイル名等はその [CODE(MIME)[multipart/mixed]]
内のそれぞれの[[本体部分]]の情報として付与します。
[34] 提出ファイルが複数の時の [CODE(MIME)[multipart/mixed]] 内の本体部分では
[CODE(MIME)[Content-Disposition: file]] とするかのような記述が仕様書にあります
[SRC[HTML 4.01 17.13.4.2]] が、
[CODE(MIME)[attachment]] の誤りだそうです [SRC[HTML 4.01 正誤表 10.]]。
[WEAK[みっともないことに HTML 4.01 正誤表は [[RFC 2388]] に責任転嫁しております(w。確かに元々 HTML 4.0 では [CODE(MIME)[attachment]] になっておりましたが、 HTML 4.01 で [Q[minor typo]] として修正されています [SRC[HTML 4.01 A.1.3]]。]]
[[#comment]]
*** ファイル情報 (ファイル名など)
[44] フォーム・ソフトウェアはファイル名やその他のファイルの属性情報をつけても構いません。
[SRC[RFC 2388 4.4]]
[33] HTML UA は提出する各ファイルにファイル名を供給するよう試みるべきです。
ファイル名は [CODE(MIME)[Content-Disposition]] 欄の
[CODE(MIME)[[[filename]]]] 引数で指定します。[SRC[HTML 4 17.13.4.2]]
UA 側システムのファイル名が [[US-ASCII]] でないときには、
ファイル名は近似するか、 RFC 2045 の方法で符号化しなければなりません。
[SRC[HTML 4 17.13.4.2]] と >>29 に続いてここでも [Q[RFC 2045の方法]]が出てきましたが、
こちらもやはり RFC 2045 に規定はありません。
RFC 2388 は、 RFC 2045 ではなく、 [[RFC 2231]] の方法を使っても良いとしています。
[SRC[RFC 2388 4.4]] この規定は RFC 2231 とは整合していますが、
[CODE(ABNF)[encoded-word]] を使うべしとする [CODE(MIME)[name]]
引数の規定 (>>29) とは矛盾しています。本当に使い分けろというのでしょうか。
[45]
提出するファイルは相互にファイル名で参照関係を持っているかもしれませんから、
ファイル名が保存されていると便利です。 [SRC[HTML 4 17.13.4.2, RFC 2388 4.4]]
ファイル名指定に関する様々な問題については、
[CODE(MIME)[[[filename]]]] 引数の説明をご覧ください。
[[#comment]]
** 遠隔ファイル指示子
[49] 遠隔ファイルを直接送らずに、 [CODE(MIME)[[[message/external-body]]]]
を使ってその[[指示子]]だけを送ることができます。
[SRC[RFC 2388 5.3]]
[52]
[CODE(MIME)[message/external-body]] の使い方は色々ありますが、
[CODE(MIME)[[[access-type]]]] [CODE(MIME)[[[uri]]]]
を使って遠隔ファイルの
[[URI参照]]を送るのが現代的でよろしいのではないでしょうか。
[[#comment]]
* 他との関係
** HTML と [CODE(MIME)[multipart/form-data]]
[6] HTML のフォームでは [CODE(MIME)[[[application/x-www-form-urlencoded]]]]
もよく使われていますが、任意のバイナリ・データや非 ASCII
文字を効率よく確実に扱うことができないという問題があります。
バイナリ・データや非 ASCII 文字を含むフォームの提出では、
[CODE(MIME)[multipart/form-data]] を使うべきです [SRC[HTML 4 17.13.4.2]]。
ファイル選択制御子 ([[[CODE(HTMLe)[input]]/[CODE(HTML)[file]]]])
を使う時には、 [CODE(MIME)[multipart/form-data]] を
[CODE(HTMLe)[form]] の [CODE(HTMLa)[enctype]] で指定するべきです
[SRC[HTML 4 17.3, 17.13.4.2]]。
[35] HTML のフォームで [CODE(MIME)[multipart/form-data]] で提出させたい時は、
[CODE(HTMLe)[[[form]]]] 要素の [CODE(HTMLa)[[[enctype]]]]
属性に [CODE(MIME)[multipart/form-data]] と指定しておきます。
各本体部分の文字符号化方式の決定には、 [CODE(HTMLe)[form]]
要素の [CODE(HTMLa)[[[accept-charset]]]] 属性の指定を参照します。
[[#comment]]
** 転送プロトコルと [CODE(MIME)[multipart/form-data]]
[54] [[MIME]] の規定によれば、 [CODE(MIME)[[[multipart/[VAR[*]]]]]]
のすべての[[実体]]の [CODE(MIME)[[[Content-Transfer-Encoding]]]]
は [CODE(MIME)[[[7bit]]]], [CODE(MIME)[[[8bit]]]], [CODE(MIME)[[[binary]]]]
のいずれかでなければなりません。もちろん [CODE(MIME)[multipart/form-data]]
の実体にも適用されます。
注意: [CODE(MIME)[multipart/form-data]]
の''中''の[[本体部分]]についての規定では''ありません''。
本体部分の [ABBR[CTE]] については >>53 を参照して下さい。
[55] [[HTTP]] では [CODE(MIME)[Content-Transfer-Encoding]]
を使用しません (常に [CODE(MIME)[binary]] 相当です) が、
[CODE(HTTP)[[[Content-Encoding]]]] と [CODE(HTTP)[[[Transfer-Encoding]]]]
があります。 [CODE(HTTP)[Transfer-Encoding]] は媒体型に依存しませんので、
[CODE(MIME)[multipart/form-data]] であろうがそうでなかろうが常に使用できます。
[CODE(HTTP)[Content-Encoding]] が使用できるのかどうかは微妙なところですが、
特別規定がないのですから、使用できるのでしょう。但し、
それに対応している実装 (クライアント・鯖) がどれだけあるのかは微妙なところです。
[56] [CODE(MIME)[[[Content-MD5]]]] による簡易的な整合性情報は、
MIME では [CODE(MIME)[multipart/[VAR[*]]]] に対して使用することが認められて''いません''が、
HTTP では認められています。 [CODE(MIME)[multipart/form-data]]
についても例外ではありません。
しかし、 [CODE(MIME)[multipart/form-data]] 全体の [[MD5]]
ハッシュを計算するよりは、面倒でも個々の本体部分で計算した方が良いでしょう。
もし HTTP で提出された [CODE(MIME)[multipart/form-data]] が途中で
MIME に変換されて [WEAK[(例えば電子メイルで)]]
送られるとすると困ったことになります。
[[#comment]]
** その他
[[#comment]]
* 安全性
[50] [CODE(MIME)[multipart/form-data]]
を構成するプロトコル要素や[[フォーム]]の仕組み自体には、
様々な安全上の問題があることが知られています。
例えば、利用者の意図しない状態や利用者が十分な考慮を行えない状況で自動的・
半自動的にフォームを提出させると、
利用者の私的な情報や利用者の環境の安全に関わる情報が送信されてしまう虞があります。
このほかにも、フォームの提出という仕組みそのものに起因する問題が多く見つかっています。
また、ファイルを提出する際には [CODE(MIME)[filename]]
引数を使うことができますが、フォーム処理エージェント
([CODE(MIME)[multipart/form-data]] を処理する側)
が信頼して無防備に実際のファイル名等として使用すると、
既存の別のファイルやシステム・ファイルを上書きしたり、
その環境で扱えないファイル名のファイルが中途半端にできてしまったりする虞があります。
詳しくは [CODE(MIME)[[[filename]]]] 引数の説明をご覧ください。
このようなフォーム自体や [CODE(MIME)[multipart/form-data]]
が利用しているプロトコル要素に関する問題や、
特定の実装に依存した問題を除いては、 [CODE(MIME)[multipart/form-data]]
に関する安全上の問題は見つかっていません。
[57] 提出の途中での改竄を検出する簡易的な手段として
[CODE(MIME)[[[Content-MD5]]]] が使用できます (>>56)。
但し記述された [CODE(MIME)[Content-MD5]] 値自体が改竄されることもあり得ますから、
あくまで簡易的なものです。また、
実装している[[利用者エージェント]]は現時点で存在しないと思われます。
[58] 一般の MIME の実体の安全のための仕組みとして[[署名]]のための
[CODE(MIME)[[[multipart/signed]]]] や[[暗号化]]のための
[CODE(MIME)[[[multipart/encrypted]]]] が、
それを使った実際のシステムとして [[PGP/MIME]] や [[S/MIME]]
があります。しかし、現実に [CODE(MIME)[multipart/form-data]]
と組合せて使っている (使える) 例は聞いたことがありません。
[CODE(MIME)[multipart/form-data]] のどの部分を署名・暗号化するのか
(あるいは全体をするのか) や、フォームの提出の手続きの中でどのように処理するのかなどの詳細な標準化がなされないと
(または[[デファクト標準]]が登場しないと) 使用するのは難しいでしょう。
[59] 現実にフォームの提出の安全のために使用されているのは
[[TLS]] や [[SSL]] です。 [[HTTP]] に対応した利用者エージェントや鯖では大抵
TLS over HTTP ([[HTTPS]]) が利用できるので、
[[フォーム処理エージェント]]としては特別な処理が要らないのが普通です。
但し、 HTTP 以外の提出方法 (特に電子メイル)
にはこの方法は使えません。
[[#comment]]
* 適合性
[10] HTML 4 UA は、 [CODE(MIME)[multipart/form-data]]
によるフォームの提出を実装しなければなりません [SRC[HTML 4 17.13.4]]。
[[#comment]]
* 実装
[15] WinIE 3.02 用の file upload add-on は1997年の中ごろに出ました。
[36] 現代のほとんどの [[WWWブラウザ]]は [CODE(MIME)[multipart/form-data]]
によるフォームの提出を実装しています。
[37]
一方、 [[CGIスクリプト]]などの鯖側は酷い状況です。
多くの実装は相手にもしていません。 [[Perl]] なら [CODE(file)[[[CGI.pm]]]]
などを使えば自動的に対応できますが、[WEAK[最近は増えてきたとはいえ]]モジュールを
CGI スクリプトで使うことは少なく、 [CODE(MIME)[application/x-www-form-urlencoded]]
にしか対応していません。ファイルのうpがしたくなったら
(素直にモジュールを使えばいいのに) 見よう見まねで適当に対処しようとして、
結局特定ブラウザの特定の版でしか上手く動かないようなコードを書いてみたり。
[WEAK[(で、質問掲示板で暴れてみたり。)]] お前らちゃんと仕様書読んでくださいよ。
処理系で標準または標準に近いモジュール的なものが[[要求]]の解析をしてくれることが広く知られていて、
そのモジュール的なものの作者がちゃんと仕様を読んでコードを書くような人なら、
その処理系で書かれた処理はさほど深く考えなくても自動的に
[CODE(MIME)[multipart/form-data]] を正しく処理できるはずです。
[WEAK[よく知りませんけど、 Java servelet とか PHP はその辺きちんとしてるのではないですか?]]
[[#comment]]
** 媒体型と charset
[1] [[WinIE]] も [[Mozilla]] も [[Opera]] も、 [CODE(MIME)[multipart/form-data]]
に含まれる[[本体部分]]には [[charsetパラメーター]]を付けてくれません。
(ファイル送信を除いて [CODE(MIME)[[[Content-Type]]]] 欄そのものをつけません。)
[2] Opera は、 [CODE(MIME)[multipart/form-data]] そのものに存在しない
[CODE(MIME)[charset]] 引数をつけてきます。
この charset 値は実際にはそれに含まれる本体部分の[[実体本体]]及び
[CODE(MIME)[[[Content-Disposition]]]] 欄の [CODE(MIME)[[[name]]]]
引数に適用されるようです。あ、 [CODE(MIME)[[[filename]]]] にもかな?
今度確かめてみよう。
[3] 規格不適合ながらもとりあえず >>2 のように情報を送ってくる Opera
に対して、 WinIE と Mozilla は既定では何もしません。ただし、
[CODE(HTML)[[[_charset_]]]] hack を使えば一応は情報を得られます。
[4] >>2-3 の情報は、 [CODE(HTML)[[[file]]]]
として送られる実体本体には適用できません。 (その実体の頭欄には適用されます。)
[CODE(MIME)[charset=[[unknown-8bit]]]] とでも考えるしかなさそうです。問題は、
一般の form data と file を区別する確実な方法がないことです。
IE, Moz, Opera に限れば、 [CODE(MIME)[filename]] 引数の有無で決定できますが。。。
[5] >>4 あ、確実な方法が1つだけあります。受取る側が名前を知っていること。
これ超確実。
[[#comment]]
** 複数ファイルをまとめて提出
[13] 1つのファイル選択制御子 ([[[CODE(HTMLe)[input]]//[CODE(HTML)[file]]]])
を使って複数ファイルをうpする (>>51) のは、 [[UA]] で対応してるのはなさげ、
サーバーもおそらく全滅だろうという感じですね。
[[www-html]] で [[Opera]] の特定の版では出来るという未確認情報がありましたが、
最新版では出来ないらしいし、勘違いかなんかじゃないかなあ。
[[#comment]]
** 零個のファイルを提出
[6] ファイル選択制御子 ([[[CODE(HTMLe)[input]]/[CODE(HTML)[file]]]])
があっても、ファイル名として何も指定されなかった場合、
WinIE も Mozilla も Opera も、空の内容を送ります。
このとき、 WinIE と Mozilla は頭欄に
[CODE(MIME)[[[Content-Type]]: [[application/octet-stream]]]] と書いてきて、
[CODE(MIME)[[[Content-Disposition]]]] にも [CODE(MIME)[[[filename]]=""]]
がつきます。 Opera ではどちらもつかず、本当に空
([CODE(MIME)[Content-Disposition: [[form-data]]; name=[VAR[名前]]]]
と空の内容だけ) になります。
[38] 頭欄がどうであれ、空の実体を送ってしまうと [WEAK[(一般の UA の場合に)]]
ファイル未選択状態と内容が空のファイルを提出した場合が区別できなくなってしまいます。
ファイルを選択していないファイル選択制御子はそもそも[[成功]]にしてはいけないのではないでしょうか。
[[#comment]]
* 例
[39] HTML のフォームの例 [SRC[HTML 4 17.13.4.2、改]]
[PRE(HTML)[
]PRE]
このフォームで、文章入力欄に [SAMP[Larry]] と記入し、ファイル選択で
[SAMP(file)[file1.txt]] を指定して提出した場合 [SRC[HTML 4 17.13.4.2, 改]]:
[PRE(MIME)[
Content-Type: multipart/form-data; boundary=AaB03x
''''''
--AaB03x
Content-Disposition: form-data; name="submit-name"
''''''
Larry
--AaB03x
Content-Disposition: form-data; name="files"; filename="file1.txt"
Content-Type: text/plain
''''''
[VAR[... contents of file1.txt ...]]
--AaB03x--
]PRE]
更に [SAMP(file)[file2.gif]] も選択していた場合 [SRC[HTML 4 17.13.4.2, 改]]:
[PRE(MIME)[
Content-Type: multipart/form-data; boundary=AaB03x
''''''
--AaB03x
Content-Disposition: form-data; name="submit-name"
''''''
Larry
--AaB03x
Content-Disposition: form-data; name="files"
Content-Type: multipart/mixed; boundary=BbC04y
''''''
--BbC04y
Content-Disposition: [DEL[file]] [INS[attachment]]; filename="file1.txt"
Content-Type: text/plain
''''''
[VAR[... contents of file1.txt ...]]
--BbC04y
Content-Disposition: [DEL[file]] [INS[attachment]]; filename="file2.gif"
Content-Type: image/gif
Content-Transfer-Encoding: binary
''''''
[VAR[...contents of file2.gif...]]
--BbC04y--
--AaB03x--
]PRE]
[47] ユーロ通貨記号を値に使った例 [SRC[RFC 2388 4.5、改]]
[PRE(HTML)[
Content-Type: multipart/form-data; boundary="AaB03x"
''''''
--AaB03x
content-disposition: form-data; name="field1"
content-type: text/plain; charset=windows-1250
content-transfer-encoding: quoted-printable
''''''
Joe owes =80100.
--AaB03x--
]PRE]
[[#comment]]
* Q & A
[63] '''Q: HTML でファイルをアップロードするにはどうしたらいいですか? ファイル名しか取得できません...'''
A: ファイル名しか取得できないのは、
[CODE(MIME)[[[application/x-www-form-urlencoded]]]]
を使用しているからの可能性が高いと考えられます。
フォームの提出で [CODE(MIME)[[[multipart/form-data]]]]
を使うようにしましょう。
関連: >>61, [[[CODE(HTMLe)[input]]//[CODE(HTML)[file]]]], [[提出]]
[61] '''Q: HTML によるフォームの提出でブラウザに [CODE(MIME)[multipart/form-data]] で送ってもらうにはどうしたらいいですか?'''
A: [CODE(HTMLe)[[[form]]]] 要素の [CODE(HTMLa)[[[enctype]]]]
属性を [CODE(MIME)[[[multipart/form-data]]]] と指定してください。
ついでに、 [CODE(HTMLa)[[[accept-charset]]]] 属性に希望する[[文字コード]]も指定しておきましょう。
[CODE(HTMLa)[[[method]]]] 属性を [CODE(HTML)[[[post]]]]
にしておくのを忘れないように。
関連: [CODE(HTMLe)[[[form]]]], [CODE(HTMLa)[[[enctype]]]], [[提出]]
[62] '''Q: CGI スクリプトで [CODE(MIME)[multipart/form-data]] と [CODE(MIME)[application/x-www-form-urlencoded]] を見分けるにはどうしたらいいですか?'''
A: [[CGI]] には [CODE(CGI)[[[CONTENT_TYPE]]]] という[[メタ変数]]
([[環境変数]]) があります。その値で判別できます。
[CODE(CGI)[CONTENT_TYPE]] の値の先頭の19文字が
[CODE(MIME)[multipart/form-data]] [WEAK[(大文字・小文字の区別なし)]]
で、その次の文字が存在しないか、[[空白]] ([[間隔]]、
[[タブ]]、[[改行]]) か、[[セミコロン]] ([CODE(MIME)[;]])
なら、 [CODE(MIME)[multipart/form-data]] が使われています。
[CODE(CGI)[CONTENT_TYPE}] の値の先頭35文字が]]
[CODE(MIME)[application/x-www-form-urlencoded]]
[WEAK[(大文字・小文字の区別なし)]]
で、その次の文字が存在しないか、[[空白]] ([[間隔]]、
[[タブ]]、[[改行]]) か、[[セミコロン]] ([CODE(MIME)[;]])
なら、 [CODE(MIME)[application/x-www-form-urlencoded]] が使われています。
それ以外なら、未知の何かが使われています。
関連: [CODE(MIME)[[[Content-Type]]]], [CODE(CGI)[[[CONTENT_TYPE]]]]
[[#comment]]
* メモ