はじめに
Webアプリからのファイル流出事件は大騒ぎになってしまうことが多いです。素人から見ても熟練者から見ても、この事象はただのバグです。しかし、製造工程のメンバーにとある知識が欠落していることで、びっくりするくらい簡単にこのバグは埋め込まれてしまいます。
今回はこのバグを防ぐコツを書いてみたいと思います。
実装者に確認すべきこと(コードレビュー)
コードレビューで確認すべきことのうち、最も大切なのは次の事項だと思います。
「ファイルをレスポンスするためのコードを書いたか(どのコードを経由してファイルがレスポンスされているか理解しているか)」
※ファイルに対してアクセスコントロールが必要な前提で書いています。
ここさえ押さえておけばあとは些細な問題です。逆に言えば、ここを間違えてしまうことで大事件が起こってしまうのです。
具体的なコード片としては、PHPの場合は、
/* PDFをレスポンスするサンプル */
header("Content-type: application/pdf; charset=UTF-8");
header('Pragma: cache');
header('Cache-control: max-age=2592000, must-revalidate');
echo file_get_contents('file.pdf');
のようなコードを書きますし、プレーンなPHPではなく、例えばLaravelを使用しているならば、
response()->file($filePath);
のような便利なラッパー関数が用意されています。
では、なぜこれらのコード片が重要になるのかを解説します。
「サーバサイドプログラムでファイルへのアクセスコントロールをするには、サーバサイドプログラムがファイルをレスポンスする以外の方法が難しい」
ということです。先ほどのコードの断片で言えば、
/* アクセスコントロールを行うならばここより前!!! */
/* PDFをレスポンスするサンプル */
header("Content-type: application/pdf; charset=UTF-8");
header('Pragma: cache');
header('Cache-control: max-age=2592000, must-revalidate');
echo file_get_contents('file.pdf');
上記のコメントした箇所、あるいはそれより前に、ファイルをレスポンスして良いかどうかのチェックを実装する必要があります。例えばDBのチェックです。そして、サーバサイドでアクセスコントロールのチェックをした後に、ファイルのレスポンスを行っています。
HTTPサーバ管轄下にアクセスコントロールが必要なファイルを置かない
では、実装者が「ファイルをレスポンスするコードに心当たりがない」ときに事件が起こってしまうケースを紹介します。最も起こりやすいケースはファイルをHTTPサーバ管轄下のディレクトリに入れてしまうことです。
サーバサイドプログラムは、アップロードされたファイルを
・HTTPサーバ管轄外のディレクトリに保存する
・HTTPサーバ管轄のディレクトリに保存する
の2つの実装パターンのどちらも可能であることが多いです。大切なファイルをHTTPサーバ管轄のディレクトリに置いてしまうと外部に漏れてしまう原因になるのです。
HTTPサーバの基本動作は、URLに対応したファイルがあればそれをレスポンスする動作です。サーバサイドのプログラムを経由せずにファイルがレスポンスされてしまいます。何かコントロールをしたくても、HTTPサーバのconfigを頑張って設定するくらいしかできることがありません。
さて、先述の記載を振り返ってみます。
「ファイルをレスポンスするためのコードを書いたか(どのコードを経由してファイルがレスポンスされているか理解しているか)」
このように書きました。何故この観点が大切なのかを、HTTPサーバの基本動作と組み合わせて考えてみます。
・HTTPサーバ管轄下にファイルを設置した場合はサーバサイドプログラムからのコントロールが難しい
・HTTPサーバ管轄外のファイルはサーバサイドにコードを書かないと取り出すことが難しい
この2行で説明は十分だと思います。サーバサイドプログラムのどこのソースコードが動いてファイルがレスポンスされるのかを知っている、ということは、すなわちHTTPサーバ管轄外にファイルを設置した可能性が高いわけです(もちろん断定できる要素ではないので別途検査は必要です)。
そして、ファイルをレスポンスするコードに心当たりがないということは、すなわちHTTPサーバ管轄下にファイルを設置してしまった可能性が高くなります。
その他気を付けるべきこと
例えば、コードレビューを行っている際、
・チュートリアル通りやったから大丈夫
・フレームワーク同梱のコマンドで設定したからよくわからない
のようなコメントが実装者から出てきたとします。これは先述した
「ファイルをレスポンスするためのコードを書いたか(どのコードを経由してファイルがレスポンスされているか理解しているか)」
に答えられていません。ファイルをレスポンスするためにコードを書いたか否か、が大事件の火種になることを知らない可能性があります。教えてあげましょう。
なお、
・チュートリアル通りやったから大丈夫
に関しては、コンテンツがインターネット公開のブログみたいな内容だと、アクセスコントロールを実装せずにファイルをインターネットに公開してしまっている可能性が十分考えられますし、
・フレームワーク同梱のコマンドで設定したからよくわからない
に関しては、一旦ファイルをHTTPサーバ管轄外のディレクトリに避けたにも関わらず、内部的にはシンボリックリンクでHTTPサーバ管轄と同義の設定にしてしまっていることが考えられます。
上記のように、「よくわからないけど動く」 という状況に関しても、ファイルをレスポンスするためにどこが動いてるのか、を説明できることが大切になります。
以上を理解したら
Webアプリで扱うファイルはインターネットで公開しても良いファイルから、特定の人にしか見せてはいけないものまで多岐に渡ります。初めからインターネット公開で大丈夫なファイルはサーバのリソース削減のためにHTTPサーバ管轄に設置しても良いでしょう。アクセスコントロールが必要ならば、サーバサイドプログラムからしかアクセスできないところに設置しましょう。
ファイルをWebアプリで公開するというシンプルなテーマでも、実装する工程で生じる選択肢が意外とあります。そして、ファイルを扱うという性質上、選択肢の間違いが許されないケースの方が多いと思います。気を付けましょう。