プログラムを書く上で、一時ファイルを作る必要のある場面もあるかと思いますが、作り方に気をつけないと、セキュリティ上などの懸念が生じてしまうことにもなります。
この記事のスコープ
この記事では、「(子プロセスなどで使う場合も含め)自分のプロセスで値を使い終える」一時ファイルについてのみ考慮します。「プロセス終了後に残しておいて、別なプロセスがあとから読み取る」ような一時ファイルについては、作成時にできることにも限りがあります。
また、セキュリティ面については、主に「同じマシンにいる、別な非rootユーザーからの攻撃」を考えていきます。root権限を持ったユーザーに対してや、同じユーザーで動く別プロセスなどに対しては、できることにも限界があります。
そして、Unix系のパーミッション環境を前提としますので、Windowsではそのまま適用できるとは限りません。
作成場所
まずは、作成する場所を考える必要があります。
専用の一時ディレクトリ
パーミッションを700
に設定したディレクトリを用意しておけば、この中にはrootでない他人がアクセスすることはできません。他人にはファイルの有無すら知ることもできませんので、普通にファイルアクセスを行っても、共用の一時ディレクトリを使うより格段に安全性が向上します。
なお、ディレクトリに1つでもファイルが入っていれば、そのファイルの削除ができない結果、ディレクトリも他人には削除不可となりますが、空ディレクトリは親ディレクトリ側のパーミッションで削除できるかが決まります。不正な削除が可能とならないように注意が必要です。
専用の一時ディレクトリを用意する方法として、サーバ構築などの段階で作り付けで用意しておく方法や、共用の一時ディレクトリにサブディレクトリを作成する方法が考えられます。
共用の一時ディレクトリ
共用の一時ディレクトリの場合、スティッキービットが立っているディレクトリを使う必要があります。
単に777
のディレクトリでは、他人からのファイルの不正な読み書きは作成時のパーミッション設定で防ぐことが可能ですが、他人が削除や名前変更することはできてしまいます。スティッキービットを立てることで、ファイルの所有者・ディレクトリの所有者・root以外にはそれらの操作が不可能となります。
ファイル作成時
ファイル作成時には、以下のようなことに注意が必要です。
- 特に必要性があるのでもなければ、ファイル名を固定にしない
- 作成時にパーミッションを指定する(あとから指定すると、作成とパーミッション設定の間に第三者のアクセスが入る危険がある)
- 「すでにファイルが存在すれば失敗する」という設定でファイルを新規作成する(事前にファイルを用意して待ち構える攻撃への対策)
競合状態への対応
上の例にもありますが、「ある条件」を満たしていることをチェックしてから、その条件を前提としたアクセスを行うと、チェックと実行の間に第三者が介入する危険があります(特に、共用のテンポラリディレクトリでは第三者にもファイルの作成が可能です)。「チェックしつつ実行するような指定を行って介入の隙を作らない」あるいは「チェックしたファイルと操作をしようとするファイルが同一であることを確認する」などの防衛が必要になります。
即時unlink
子プロセスに一時ファイルのファイル名を渡す必要があるような場面では使えないですが、一時ファイルを1つのプロセスだけでしか使わない場合、「ファイルを開いたままunlink
する」という技が有効です。
unlink
はファイルの名前を削除する機能なので、すでに開いたファイルハンドルは有効なままですが、unlink
後には二度とそのファイルを開くことができなくなります。そして、アクセス可能なファイルハンドルがなくなれば、ファイルは自動で削除されます。
後始末の自動化
ファイルを作って処理の最後に削除、のような流れを手で書いていると、途中で例外が起きたような場合に一時ファイルが残ってしまいます。対応する方法があるなら事前対応するほうがいいでしょう。
- プロセス内でしか使わない一時ファイルであれば、即時
unlink
することで、たとえ処理系がクラッシュするようなことになってもファイルが残らなくなります。 - 言語によっては、一定のコードブロックからどんな形であれ脱出した場合に、後処理を自動で実行する構造があります(Javaのtry-with-resource、C++のRAII、Rubyのブロック付きメソッドなど)。一時ファイルについても、これらの構造がマッチするのであれば使うと便利です。