一時ファイル(temporary file)は次に挙げる用途に用いられる。
- プロセス間のデータ共有
- 予備のプログラムデータの保存 (たとえばメモリ内容の保存)
- クラス、JAR ファイル、ネイティブライブラリの動的な作成や読込み
一時ファイルも「ファイル」であるため、ファイル操作に関連するその他のルールにも適合しなくてはならない。「FIO00-J. 共有ディレクトリにあるファイルを操作しない」や 「FIO01-J. 適切なパーミッションを設定してファイルを作成する」など。その上で、一時ファイルは、プログラムの終了前に削除されなくてはならない。
不要になった一時ファイルを削除することで、ファイル名やその他のシステムリソース(二次記憶)をリサイクルできる。プログラムは、正常な動作時には一時ファイルを削除する責任がある。プログラムが異常終了した場合、「みなしご」になったファイルを確実に削除する方法は存在しない。たとえ finally 節を使ってメソッドを呼び出したとしても、finally 節が実行される保証はない。そのため、一時ディレクトリや不要になったファイルを消去するユーティリティプログラムを動かすシステムが多い。こうしたユーティリティプログラムは、システム管理者が手作業で起動することもあれば、システムのプロセスが定期的に起動することもある。しかし、ユーティリティプログラム自身もまた、しばしばファイル関連の攻撃の対象となる。
違反コード
以下のコード例では、ファイルはセキュアなディレクトリに作成され、「FIO00-J. 共有ディレクトリにあるファイルを操作しない」に適合していると仮定する。また、適切なパーミッションを設定したファイルが作成され、 「FIO01-J. 適切なパーミッションを設定してファイルを作成する」にも適合していると仮定する。どちらの要求事項も JVM の外部で実現することが可能である。
以下の違反コード例では、不要になった一時ファイルを削除していない。
class TempFile { public static void main(String[] args) throws IOException{ File f = new File("tempnam.tmp"); if (f.exists()) { System.out.println("This file already exists"); return; } FileOutputStream fop = null; try { fop = new FileOutputStream(f); String str = "Data"; fop.write(str.getBytes()); } finally { if (fop != null) { try { fop.close(); } catch (IOException x) { // エラー処理 } } } } }
違反コード (createTempFile()、deleteOnExit())
以下の違反コード例では File.createTempFile() メソッドを呼び出し、接頭辞(prefix)と拡張子(extension)の2つの引数に基づき一意の一時ファイル名を作成している。File.createTempFile() は、一意のファイル名を作成するために設計されたメソッドであるが、作成されるファイル名は容易に予測可能である。ランダムなファイル名が必要な場合は、乱数生成器を使って接頭辞を作成するとよい。
以下のコード例では deleteOnExit() メソッドを使用し、JVMの終了時に一時ファイルの削除も行っている。しかし、Java API 仕様 [API 2006] には、File クラスの deleteOnExit() メソッドについて以下のように記されている。
削除は、『Java言語仕様』で定義されているように、仮想マシンが正常終了する場合にだけ試行される。削除がいったん要求されると、その要求は取り消せない。そのため、このメソッドの使用には注意が必要である。
注:このメソッドをファイルロックのために使用してはならない。結果として得られるプロトコルを確実に動作させることができないからである。
すなわち、JVM が異常終了した場合にはファイルは削除されない。Bug ID: 4171239 [SDN 2008] には、Windows システムに長い間存在したバグについて報告されているが、このバグは、ストリームや RandomAccessFile がクローズされる前に deleteOnExit() が呼び出されると JVM のファイル削除が失敗するというものであった。
class TempFile { public static void main(String[] args) throws IOException{ File f = File.createTempFile("tempnam",".tmp"); FileOutputStream fop = null; try { fop = new FileOutputStream(f); String str = "Data"; fop.write(str.getBytes()); fop.flush(); } finally { // Stream/file はまだオープンな状態 // Windows ではファイルは削除されないだろう f.deleteOnExit(); // JVM の終了時にファイルを削除する if (fop != null) { try { fop.close(); } catch (IOException x) { // エラー処理 } } } } }
適合コード (Java SE 7, DELETE_ON_CLOSE)
以下の適合コードでは、Java SE 7 の NIO2 パッケージが提供するメソッドを使って一時ファイルを作成している。同パッケージの createTempFile() メソッドは予測不可能なファイル名を作成するが、ファイル名を作成する実際のメソッドは実装依存であり、明示されていない。ファイルのオープンには「try-with-resources」構文を用い、例外発生の有無に関係なくファイルを自動的にクローズする。さらに、Java SE 7 の DELETE_ON_CLOSE オプションを指定してファイルをオープンしており、ファイルはクローズ時に自動的に削除される。
class TempFile { public static void main(String[] args) { Path tempFile = null; try { tempFile = Files.createTempFile("tempnam", ".tmp"); try (BufferedWriter writer = Files.newBufferedWriter(tempFile, Charset.forName("UTF8"), StandardOpenOption.DELETE_ON_CLOSE)) { // ファイルにデータを書き込む } System.out.println("Temporary file write done, file erased"); } catch (FileAlreadyExistsException x) { System.err.println("File exists: " + tempFile); } catch (IOException x) { // パーミッション違反などその他のエラー System.err.println("Error creating temporary file: " + x); } } }
適合コード
一時ファイルを保存するためにセキュアなディレクトリを使用することができない場合、以下に挙げる仕組みを使用することで、危険なディレクトリに一時ファイルを保存することで発生する脆弱性を回避できる。
- ソケットやリモートプロシージャコールなどその他のIPCメカニズム
- 低レベルの Java Native Interface (JNI)
- メモリマップドファイル
- 同一のJVM内のヒープデータを共有するスレッド(Javaのプロセス同士のデータ共有のみに適用可能)
リスク評価
一時ファイルを削除してからプログラムを終了しないと、情報漏えいやリソースの枯渇につながる恐れがある。
ルール | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
---|---|---|---|---|---|
FIO03-J | 中 | 中 | 中 | P8 | L2 |
関連ガイドライン
CERT C Secure Coding Standard | FIO43-C. Do not create temporary files in shared directories |
CERT C++ Secure Coding Standard | FIO43-CPP. Do not create temporary files in shared directories |
MITRE CWE | CWE-377. Insecure temporary file |
CWE-459. Incomplete cleanup |
参考文献
[API 2006] | Class File, methods createTempFile, delete, deleteOnExit |
[Darwin 2004] | 11.5 Creating a Transient File |
[J2SE 2011] | |
[SDN 2008] | Bug IDs: 4171239, 4405521, 4635827, 4631820 |
[Secunia 2008] | Secunia Advisory 20132 |
翻訳元
これは以下のページを翻訳したものです。
FIO03-J. Remove temporary files before termination (revision 89)