ファイルの検査と使用の間に生じる競合状態

ファイルの検査と使用の間に生じる競合状態

説明

ファイル名を使って存在やアクセス可否を調べた後、同じパスを開き直したり書き込んだりすると、その間に攻撃者がファイルを置き換える可能性があります。特に一時ディレクトリや共有ディレクトリでは、シンボリックリンクや先回りしたファイル作成によって、意図しないファイルを上書きするおそれがあります。

想定される影響

  • 検査後のパスをシンボリックリンクに置き換えられ、機密ファイルを上書きする可能性があります。
  • 存在の確認から作成までの間に、別のプロセスがファイルを作成する可能性があります。
  • 読み取り前の検査結果と使用時の状態が異なり、情報漏えいや認可の回避につながるおそれがあります。

対処方法

  • 同じファイル名を再検査せず、ファイルディスクリプターを使う API を利用してください。
  • 新規ファイルは O_CREAT | O_EXCL などのフラグでアトミックに作成してください。
  • 一時ファイルは専用 API を使い、非公開のディレクトリ内で作成してください。

例

変更前

javascript
const fs = require("fs");
const os = require("os");
const path = require("path");

const targetPath = path.join(os.tmpdir(), "report.txt");
if (!fs.existsSync(targetPath)) {
  fs.writeFileSync(targetPath, "report");
}

変更後

javascript
const fs = require("fs");
const os = require("os");
const path = require("path");

const targetPath = path.join(os.tmpdir(), "report.txt");
const fd = fs.openSync(
  targetPath,
  fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY,
  0o600,
);
try {
  fs.writeFileSync(fd, "report");
} finally {
  fs.closeSync(fd);
}

説明:

  • 変更前: existsSync の検査から writeFileSync の呼び出しまでに、対象パスが変わる可能性があります。
  • 変更後: アトミックな作成フラグによって、検査と作成を一つのファイルシステム操作で行います。既存のパスでは失敗するため、呼び出し元でエラーを処理する必要があります。

参考資料