Description
Checking whether a filename exists or is accessible, then reopening or writing the same path, leaves time for an attacker to replace the file. In temporary or shared directories, a symbolic link or a file created first by another process may cause an unintended file to be overwritten.
Potential impact
- An attacker may replace the checked path with a symbolic link, causing a sensitive file to be overwritten.
- Another process may create the file between the existence check and file creation.
- The file state may change after a read-access check, leading to information disclosure or an authorization bypass.
Remediation
- Use file-descriptor APIs instead of checking the same filename again.
- Create new files atomically with flags such as
O_CREAT | O_EXCL. - Create temporary files with dedicated APIs in private directories.
Examples
Before
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");
}
After
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);
}
Explanation:
- Before: The target path can change between
existsSyncandwriteFileSync. - After: Atomic creation flags combine the check and creation in one filesystem operation. An existing path causes the operation to fail, so the calling code must handle that error.