仕事で使える魔法のLAMP
第40回 ファイルのアップロードを制限する
株式会社イメージズ・アンド・ワーズ
代表取締役
山口晴広(やまぐち はるひろ)
2012/1/30
| 今回は、HTTPクライアントがアップロードしてくるファイルの扱いについて解説します。受け付けるファイルのサイズ制限や、受け取ったファイルを置くディレクトリなど、注意しなければならないことが意外に多くあります(編集部) |
POSTデータの最大サイズを制限する
第37回から、PHPの実行時設定の中でも、初期設定のまま放置しない方が良い項目を紹介し、それぞれの設定変更法を解説しています。第38回(前々回)と第39回(前回)で、HTTPクライアントが送信してくるリクエストデータに関係する設定項目について解説してきました。今回は、残った数少ない設定項目を片付け、リクエストデータの扱いに関する解説は今回でおしまいとします。
今回最初に取り上げるのは「post_max_size」ディレクティブです。このディレクティブは、POSTメソッドで受け取るリクエストデータの最大サイズを指定するものです。WebブラウザなどのHTTPクライアントからデータを送信する方法には、GETメソッドとPOSTメソッドの2つがありますが、これはPOSTメソッドに関係するものです。GETメソッドは、送信したい値をURLに付け加えて送るので、もともと大きな値は送信できません。データ量を制限する設定項目もありません。
前回も解説した通り、PHPではPOSTメソッドおよびGETメソッドででリクエストデータを受信すると、自動的にそのデータを配列変数に格納します。変数を保存する場所はメモリ空間です。巨大なデータを受信すると、それだけメモリを消費します。これは入力フォームを1つも作らなかったページにも起こりうることです。POSTで大きなデータを送り付けるだけでメモリを消費させることができます。
従って、「post_max_size」の設定値はあまり大きくするべきではありません。初期設定値は8Mbytesですが、ファイルを送信するならともかく、フォームの入力データを送信するようなアプリケーションで、このような大きなサイズのPOSTデータを送信することはまずないでしょう。もっと小さな値にしてもよいと思います。もちろん、大きなファイルを送信することを前提としたアプリケーションでは、設定値を大きくすることも考えるべきでしょう。
結局のところ、第37回で紹介した「memory_limit」と同様に、アプリケーションの処理内容次第となります。ここでは普通にフォームのデータを送信する場合だけを想定し、64Kbytesとしておきます。このように小さい値に設定しても、ファイル送信の部分だけ「post_max_size」の設定値を大きくするといったことも可能です。
では、「post_max_size」を超える大きさのデータを送信したらどうなるでしょうか? 「post_max_size」を16、つまり16byteに設定して、試してみましょう。PHPが実行できるディレクトリに、print_post.phpという名前で以下に挙げるコードを保存してください。中身は単純、$POST変数を表示するだけのものです。
<?php
print_r($_POST);
?>
post_max_sizeの設定は、データだけでなく、パラメータ名やパラメータ同士のの連結に使う文字もすべて合わせたデータのサイズを制限します。パラメータ名と値は「=」で連結し、複数のパラメータは「&」で連結しますが、その分も合わせたデータサイズを制限するのです。curlコマンドで2つのパラメータを送信してみます。
$ curl -X POST --data 'foo=bar&baz=1234' http://www3026ub.sakura.ne.jp/print_post.php
Array
(
[foo] => bar
[baz] => 1234
)
結果を見ると、2つのパラメータとそれぞれのデータが送信できていることが分かります。この例で送信した文字「foo=bar&baz=1234」は16文字。ちょうど制限に収まっています。では、1文字追加して、次のように実行してみましょう。
$ curl -X POST --data 'foo=bar&baz=12345' http://www3026ub.sakura.ne.jp/print_post.php
Array
(
)
post_max_sizeの設定値を超えてしまい、リクエストデータを確認しようにも、存在しないということになってしまいました。PHPでは、PHPでは、post_max_sizeの設定で制限した値を超えたデータを送信すると、リクエストデータすべてが無視されるためです。制限の範囲内にあるパラメータだけが処理されるというようなことはないので、大きなデータを受信するようなアプリケーションを実装する場合はこの点に留意する必要があります。
第38回で、脆弱性に対応したPHP 5.3.9が入手可能になったことを紹介しました。そして、5.3.9にアップデートできないときに、リクエストデータの最大値を制限するという方法を紹介しました。今回紹介したpost_max_sizeが、そのための設定です。
PHP 5.3.9では、「max_input_vars」という、パラメータの最大数を制限するディレクティブも導入されました。こちらも設定しておきましょう。フォームを使った一般的なアプリケーションを動かすことを想定して、100としておきます。
アップロードファイルをどこで受け付けるか?
リクエストデータはファイルを含んでいることもあります。Webブラウザから、ファイルを選択して送信するときを考えれば分かるでしょう。このようにしてファイルを受信すると、ファイルそのものは変数には格納せず、ファイル名やファイルサイズなどの情報を変数に格納します。ファイルそのものはサーバ上のファイルシステムに保存するのです。ファイルは一般にデータサイズが大きいため、メモリには置かずに、受信したらそのままファイルシステムに書き込むようになっているのです。
このようにファイルアップロード機能を利用するときに注意すべきディレクティブがいくつかあります。まず、「file_uploads」ディレクティブは、ファイルアップロード機能を有効にするかどうかを設定します。初期設定値はOnですが、使わないことが分かっているならOffにしてもよいでしょう。ただし、このディレクティブは、ディレクトリによって設定を使い分けることができません。サーバ全体でファイルアップロードを受け付けることがないということでもない限り、Onのままにしておく方が良いでしょう。
次に注意したいのが「upload_tmp_dir」ディレクティブです。これは、HTTPクライアントがアップロードしてきたファイルを格納するディレクトリを指定するものです。初期設定値は「値なし(no value)」。この場合は、標準的な一時ディレクトリを使う事になります。Linuxならば/tmpです。
ここで、皆さんにお聞きしたいことが1つあります。/tmpの実体についてです。皆さんのLinuxサーバでは、/tmpの実体はディスクでしょうか。ディスクなら、どれくらいの容量のデータまで記録できるでしょうか。連載の解説で使わせていただいているさくらインターネットのVPSでdfを実行してみました。標準OSであるCentOSでの実行結果です。
$ df -Th
Filesystem Type Size Used Avail Use% Mounted on
/dev/hda2 ext3 17G 3.1G 13G 20% /
/dev/hda1 ext3 99M 36M 58M 39% /boot
tmpfs tmpfs 753M 0 753M 0% /dev/shm
/dev/hdb1 ext3 30G 3.9G 25G 14% /home
/tmpはディスクであり、/(ルートディレクトリ)直下にあることが分かります。このような場合、/の記録容量に注意しなければなりません。限界に近づいているときに、ファイルをアップロードしてしまうと、OSの動作に支障をきたすことが考えられるのです。念のため、upload_tmp_dirの設定は、/home以下に作成したディレクトリなどに変更しておきたいところです。別ディスクである/homeであれば、仮に記録容量の限界に達してもOSの動作に悪影響を及ぼすことはありません。
ということで、/home/uploadsというディレクトリをアップロードディレクトリにすることにしましょう。次のようにApache HTTP Serverの実行権限(ここではdaemon)で書き込み可能になるようにディレクトリを作成します。
$ sudo mkdir /home/uploads
$ sudo chgrp daemon /home/uploads
$ sudo chmod g+w /home/uploads
もう1つ、別の環境を調べてみましょう。
$ df -Th
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda1 ext3 30G 2.9G 26G 11% /
tmpfs tmpfs 512M 0 512M 0% /dev/shm
none tmpfs 512M 0 512M 0% /tmp
こちらの環境は、/tmpがディスクではなく、tmpfsというものになっています。ここに作成したファイルはメモリ上に保存されます。いわゆるRAMディスクのようなものです。このようなケースでは「upload_tmp_dir」を/tmpのままにしておくと、大きなファイルを受け付けたときに、メモリをかなり消費してしまいます。必ず、別の場所、しかもディスク上を指定しましょう。ここで挙げた例では、OSのディレクトリ構成は/のみになっているので、/以下にアップロードファイルを受け付けるディレクトリを作るほかありません。モニタリングシステムなどによるディスク容量の監視が必要でしょう。
そしてもう1つ注意があります。upload_tmp_dirの設定値として、Webサーバが公開しているディレクトリの配下を指定することは絶対にいけません。アップロードしたファイルが外部からアクセスできるようになってしまうことがあります。Webサーバの公開ディレクトリが、PHP実行可能な状態ならば、PHPスクリプトファイルを送り込んでそれを実行させるということも不可能ではありません。
アップロードファイルの最大サイズ
アップロード対象のファイル1つ当たりの最大ファイルサイズを設定する、「upload_max_filesize」というディレクティブもあります。post_max_sizeと同じように、アプリケーションの処理内容やディスク容量なども合わせて考え、問題の起きない範囲でもっとも小さい値に設定しましょう。
このディレクティブを設定するときは、1つ覚えておいてほしいことがあります。post_max_sizeより小さくしなければならないのです。HTTPクライアントがファイルをアップロードするときは、POSTメソッドで送信しますので、post_max_sizeによる制限を受けるということです。
ファイルのアップロードという行為は、ほかのリクエストデータ同様に、無関係なPHPファイルに対して実行しても成功することがあります。いつものようにcurlを使えば簡単に試せます。確認してみましょう。まず、次のようにして80Mbytesのファイルを作成します。
$ dd if=/dev/zero of=80Mfile.dat bs=8192 count=10240
10240+0 records in
10240+0 records out
83886080 bytes (84 MB) copied, 0.186417 seconds, 450 MB/s
curlでファイル送信をするには「--form パラメータ名=@ファイル名」とします。
$ curl -X POST --form foo=80Mfile.dat http://www3026ub.sakura.ne.jp/phpinfo.php
これまでの連載で何度か使用した、phpinfo()を表示するPHPスクリプトに対し、80Mbytesのファイルを送信したことになります。送信中に/home/uploadsの中身を見ると、次のようにファイルをアップロードしている様子を確認できます。
$ ls -l /home/uploads
total 8204
-rw------- 1 daemon daemon 8388608 Jan 26 17:26 phpaidfT2
このファイルはPHPスクリプトが終了すると同時に消去されますが、外部から自由にファイルを作成できる状況であるということが、よく分かるでしょう。サーバ全体では、「upload_max_filesize」の値は最小の「1」にしておき、ファイルアップロードを許可するアプリケーションごとに適切な値をそれぞれ指定するのが良いでしょう。なお、設定値を0にすると制限がなくなってしまうようです。
最後にディレクティブ「max_file_uploads」を紹介します。これは、一度に送信できるファイル数の上限を決めるディレクティブです。初期設定値は20。これを変更する必要はあまりないでしょう。
まとめ
今回の解説を踏まえて、php.iniを編集すると次の通りになります。
memory_limit = 32M
max_execution_time = 30
expose_php = Off
magic_quotes_gpc = Off
variables_order = GPCS
register_long_arrays = Off
register_argc_argv = Off
post_max_size = 64K
max_input_vars = 100
upload_tmp_dir = /home/uploads
upload_max_filesize = 1
次回は、エラー表示やログに関係する設定について解説します。
仕事で使える魔法のLAMP バックナンバー 連載インデックスへ»
- 第1回 LAMP環境、自分で作りませんか?
- 第2回 サーバに接続して、一般ユーザーのアカウントを作る
- 第3回 アクセス制限の設定とCentOSのアップデート
- 第4回 sshを便利にする公開鍵暗号
- 第5回 公開鍵認証でsshを安全に使う
- 第6回 「ビルド」という作業は何を指しているのか
- 第7回 ダイナミックリンクとスタティックリンク
- 第8回 makeを使ってソフトウェアをビルドしてみよう
- 第9回 Makefileをいろいろ書き換えながらビルドしてみよう
- 第10回 ダウンロードファイルが真正なものであるかを確認
- 第11回 配布パッケージの中身と、configureの役目を知る
- 第12回 configureの設定を変更してみる
- 第13回 configureでソフトウェア固有の設定を変更してみる
- 第14回 Apache HTTP Serverのビルドを始めよう
- 第15回 ライブラリが足りなくてビルドできないときは?
- 第16回 proxyやsslのモジュールを使ってみる
- 第17回 OpenSSLをビルドしてApacheで利用する
- 第18回 Apache同梱ソフトウェアに引数を渡してビルド
- 第19回 認証DBにアクセスするライブラリを組み込む
- 第20回 サードパーティのApacheモジュールをビルドする
- 第21回 Apacheの設定ファイルを記述する前に
- 第22回 1つのサーバに複数の仮想サーバ?
- 第23回 設定ファイルや公開ドキュメントの配置を考える
- 第24回 設定ファイルを作成してApacheを動作させる
- 第25回 PHP編に突入! まずはソースをダウンロード
- 第26回 早速PHPをビルド! そしてテスト!
- 第27回 PHPテスト失敗の原因を追究する
- 第28回 エクステンションを有効にしてビルドに挑戦!
- 第29回 XMLを処理できるようにする
- 第30回 PHPでデータベースを使う準備をする
- 第31回 エクステンションの組み込み状況を確認する
- 第32回 単純なデータを管理するDBMを使えるようにする
- 第33回 PHPエクステンション組み込みの仕上げ
- 第34回 PHPスクリプトを実行できるようにする準備
- 第35回 PHPの設定ファイルを作って配置してみる
- 第36回 Apacheの設定ファイルでPHPの設定を変える
- 第37回 安全を考えてPHPの実行時設定を調整する
- 第38回 マジッククオート機能には頼らない
- 第39回 リクエストデータを受け取る変数の扱い
- 第40回 ファイルのアップロードを制限する
- 第41回 エラーメッセージをどう扱うか?
- 第42回 クライアントがアクセスできる範囲を制限する
- 第43回 PHPでセッションを利用するための設定
- 第44回 いよいよMySQL編、ソースからビルドすべきか?
- 第45回 MySQLのビルドに欠かせないCMakeを準備する
- 第46回 CMakeでMySQLをビルドしてみる
TechTargetジャパン
- OSSライセンスの採用傾向に「変化」あり (2012/5/21)
OSS普及の一翼を担ってきたライセンス、GPLファミリー採用の割合が減少傾向にあるそうです。いったいどうして? - ここからFirefoxの反撃が始まる (2012/3/12)
2月のLinux SquareのランキングではFirefoxの記事がダントツのページビューを集め、トップに立ちました - CMakeでMySQLをビルドしてみる (2012/3/8)
ここまでの作業でCMakeを使う準備が整いました。今回はひとまずMySQLをビルドしてみます - MySQLのビルドに欠かせないCMakeを準備する (2012/3/2)
今回は、MySQLをビルドするために欠かせないツール「CMake」をインストールし、使う準備をします
|
|
キャリアアップ
スポンサーからのお知らせ
- - PR -
イベントカレンダー
- - PR -
