Node.js 文件操作
1.3.1 【必须】限定文件操作的后缀范围
- 按业务需求,使用白名单限定后缀范围。
1.3.2 【必须】校验并限定文件路径范围
- 应固定上传、访问文件的路径。若需要拼接外部可控变量值,检查是否包含
..
、.
路径穿越字符。如存在,应拒绝。 - 使用
fs
模块下的函数方法时,应对第一个参数即路径部分做校验,检查是否包含路径穿越字符.
或..
。涉及方法包括但不限于:fs.truncate
、fs.truncateSync
、fs.chown
、fs.chownSync
、fs.lchown
、fs.lchownSync
、fs.stat
、fs.lchmodSync
、fs.lstat
、fs.statSync
、fs.lstatSync
、fs.readlink
、fs.unlink
、fs.unlinkSync
、fs.rmdir
、fs.rmdirSync
、fs.mkdir
、fs.mkdirSync
、fs.readdir
、fs.readdirSync
、fs.openSync
、fs.open
、fs.createReadStream
、fs.createWriteStream
- 使用express框架的
sendFile
方法时,应对第一个参数即路径部分做校验,检查是否包含路径穿越字符.
或..
- 校验时,应使用
path
模块处理前的路径参数值,或判断处理过后的路径是否穿越出了当前工作目录。涉及方法包括但不限于:path.resolve
、path.join
、path.normalize
等
const fs = require("fs");
const path = require("path");
let filename = req.query.ufile;
let root = '/data/ufile';
// bad:未检查文件名/路径
fs.readFile(root + filename, (err, data) => {
if (err) {
return console.error(err);
}
console.log(`异步读取: ${data.toString()}`);
});
// bad:使用path处理过后的路径参数值做校验,仍可能有路径穿越风险
filename = path.join(root, filename);
if (filename.indexOf("..") < 0) {
fs.readFile(filename, (err, data) => {
if (err) {
return console.error(err);
}
console.log(data.toString());
});
};
// good:检查了文件名/路径,是否包含路径穿越字符
if (filename.indexOf("..") < 0) {
filename = path.join(root, filename);
fs.readFile(filename, (err, data) => {
if (err) {
return console.error(err);
}
console.log(data.toString());
});
};
1.3.3 【必须】安全地处理上传文件名
- 将上传文件重命名为16位以上的随机字符串保存。
- 如需原样保留文件名,应检查是否包含
..
、.
路径穿越字符。如存在,应拒绝。
1.3.4 【必须】敏感资源文件,应有加密、鉴权和水印等加固措施
- 用户上传的
身份证
、银行卡
等图片,属敏感资源文件,应采取安全加固。 - 指向此类文件的URL,应保证不可预测性;同时,确保无接口会批量展示此类资源的URL。
- 访问敏感资源文件时,应进行权限控制。默认情况下,仅用户可查看、操作自身敏感资源文件。
- 图片类文件应添加业务水印,表明该图片仅可用于当前业务使用。