ファイルのアップロードの処理


閲覧者側にあるファイルをページ作者側へ送信する機構を 作ることができます。つまりファイルのuploadです。
この機構を応用すれば、画像ファイルを閲覧者から送ってもらって 展覧会を開くこともできますし、もらった画像を加工して閲覧者に送り返す 加工工場のようなこともできます。
ファイルをuploadするためのHTMLのページにはやはりFORMを使います。 前に解説したFORMとはちょっと形式が異なります。
uploadされたデータを受け取って適切に保存する機構はもちろんCGIです。
本節では upload の窓口となるHTMLのページの作り方と、 そのデータを受け付けて処理するCGIプログラムの作り方を簡単に解説します。
ただし、この受け付けCGIをいい加減に設計すると、 巨大なセキュリュティーホールが開くことになり、 ネットワーク犯罪者(cracker)の格好の標的となりますので、 十分注意して設計してください。


● Upload窓口ページの作成

FORMを使いますが、前節のFORMとは形式が異なります。
次の例は投稿者の名前と、投稿ファイルの投稿者側でのパスを 入力するFORMを作ります。

<FORM ENCTYPE="multipart/form-data" ACTION="multform.cgi" METHOD="POST">
Author: <INPUT TYPE="TEXT" NAME="author"><BR>
Path:   <INPUT TYPE="FILE" NAME="fname"SIZE=48><BR>
<INPUT TYPE="SUBMIT" VALUE="Upload">
</FORM>
Author:
Path:

TYPE=TEXT のINPUTタグは従来通りの一行書き込みフィールドを作ります。
TYPE=FILE のINPUTタグは、uploadするファイルパス名を書き込む フィールドを作ります。指定された変数 "fname" にはファイルのパス名 を除いたファイル名だけが格納されるようです。
パスは閲覧者のサイトのルートからの絶対パスなのでそれを記入するのは 大変です。なので普通はこのフィールドの横に自動的にbrowseボタンが 付加されます。このボタンを押すと、ファイル選択のためのウィンドウが 開きます。
submitボタンで、これらデータがupload用に加工(encode)されて uploadされます。


● 受け取り処理CGIの作成

先の例のFORMの "author"変数に "Naoki" と入力して、 "fname"変数に"/home/naoki/piblic_html/index.html" と 入力したとしましょう。browserがこれらのデータをencodeして CGIに送るデータは以下のようになります。

-----------------------------10625178864862561851508029952
Content-Disposition: form-data; name="author"

Naoki
-----------------------------10625178864862561851508029952
Content-Disposition: form-data; name="fname"; filename="index.html"
Content-Type: text/html

This is the first line of the file.
The data of file is not encoded any more, just located here.
This is the last line.

-----------------------------10625178864862561851508029952--

一つのINPUT項目は「 Content-Disposition: form-data;」で始まって 「--------------------」の行で終わっています。実はこの 「--------------------」の行の直前には「0x0d 0x0a」の2文字が 必ずあります。MS-DOSでのテキストファイルでは、このコードは改行を 表すのでファイル中に多く含まれますが、UNIXやMacでは バイナリファイルでない限り含まれません。そこで「0x0d 0x0a」と 「--------------------」が連続していたら項目の終りと判断することが できるでしょう。

この範囲の中で TYPE=FILE以外の変数の名前は name=""で表され、空行を 置いて、その値が行頭に置かれます。
TYPE=FILEの変数の名前も name=""で表されますが、それに続いて ; filename="" でファイルの名前が表されます。次の行でファイルの形式が 示され、空行を置いて、ファイル内容がほぼそのまま置かれ、空行で 終ります。

このとおりデータのencodeのされ方は前節での通常のFORMに比べて遥かに 簡単です。このデータからファイルの中身のデータを取り出して セーブするCGIは簡単に作れそうですが、細かい面倒なことが たくさんあります。MicroSoft Windowsでは終端コードが勝手に変換 されてしまうので、それを防ぐために、プログラム冒頭で 広域変数 _fmode の設定と次の関数を実行しておかなければなりません。

_fmode = _O_BINARY;
_setmode( 0, _O_BINARY );

uploadされたファイルを受け取ってセーブするには、そのCGIが指定の ディレクトリにファイルを書き込むことができるようpermissionまたは Sbitが設定されていないとなりません。Sbitについては次章を参照ください。

私が色々試してみたところ、upload受付用のCGIの出力メッセージが 4kbyteを越えると、そのCGIの出力関数で出力が詰まって動かなくなる ようです。なので例えば、uploadされた4kbyte以上のテキストファイル の中身を表示しようとすると、CGIが途中で動けなくなり、いつまで待っても browserにCGIの出力結果が送られてこないことになります。 この現象がhttpdの仕様なのかバグなのかはわかりません。

ファイル終端コードの検知を除いて、このデータのdecode作業は容易なので その詳細は詳しく説明はしません。ここに私が作成したupload受付用の CGIのソースを用意しておきましたので参考にしてください。 ただしセキュリュティー防護の対策は全くしていないので気を付けて下さい。

multform.cc


● Uploadの要注意事項

Uploadを受ける場合に必要なセキュリュティー防護策を いくつか紹介しておきます。
  • セーブするファイル名を投稿者に選ばせない。
    投稿者の好きな名前でファイルをセーブするようにしてしまうと、 投稿者はCGIが書き込みを行えるすべてのファイルを破壊することができます。 CGIがCGI保有者の権限で動く場合には、閲覧者は保有者のすべてのファイルを 破壊することができます。
    なのでファイル名やディレクトリ名には閲覧者の指定の名前ではなく、 CGI保有者が指定する限定したディレクトリ、他のファイルと重複しない 特殊なファイル名となるようにCGIを設計しなければなりません。
  • 実行可能ファイルを受け付けない。
    実行可能なバイナリファイルもしくはスクリプトファイル(csh,perl等) が投稿され、ただちに閲覧者によってCGIとして実行されるような ことになってはなりません。危険なコマンドを実行されてしまうからです。 CGIがCGI保有者の権限で動く場合には、閲覧者は完全に保有者の振りを してあらゆる悪事を遂行することができます。
    なのでセーブしたファイルの実行許可を取り下げるべきです。

  • 目次

    Copyright(C) by Naoki Watanabe. Oct 21st, 1995.
    渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp