动态导入路径必须可信,禁止直接使用用户输入;应通过白名单映射或条件判断限制可加载模块,同时防范路径穿越与文件泄露,并借助eslint等工具自动化检测风险。

动态导入(import())若路径受用户控制,就等于把模块加载权交给了攻击者。关键不是“能不能用”,而是“路径是否可信”——只要路径来自请求参数、URL片段、表单字段、Cookie或任何外部输入,就存在被篡改风险。
路径必须是静态或严格白名单控制
动态导入的安全底线是:目标模块位置必须在编码期就确定,不能由运行时数据决定。
- ✅ 安全写法:显式列出可加载的模块,用条件判断或映射表分发
// 正确:只允许预定义的几个模块
const allowedModules = {
'upload': './helpers/upload.js',
'report': './helpers/report.js',
'notify': './helpers/notify.js'
};
<p>const modName = req.query.type; // 来自用户输入
if (allowedModules[modName]) {
const mod = await import(allowedModules[modName]);
mod.execute();
} else {
throw new Error('Invalid module type');
}</p>
- ❌ 危险写法:直接拼接用户输入作为路径
// 错误:攻击者可传入 ../../etc/passwd 或 ./malicious.js const mod = await import(req.query.modulePath);
警惕路径穿越与任意文件读取
即使没执行代码,恶意路径也可能触发敏感文件泄露。Node.js 的 import() 虽不支持 file:// 协议,但若配合文件系统操作(如先写入再导入),攻击者可通过 ../ 回溯到配置文件、环境变量甚至源码。
- 禁止在路径中出现
..、/、\、://等字符组合 - 对输入做标准化处理后,检查解析出的绝对路径是否仍在预期目录内(例如
path.resolve(baseDir, userInput)后比对前缀) - 避免将用户输入用于任何文件类操作,包括
fs.readFile、fs.readdir、require()等
用工具在开发阶段拦截风险
人工审查容易遗漏,应借助自动化手段提前发现隐患。
- 启用
eslint-plugin-security,规则detect-non-literal-require和detect-non-literal-import可捕获非常量路径的import() - 在 CI 流程中加入 ESLint 检查,失败则阻断合并
- 对所有接收用户输入并参与路径构造的变量打上显式标记(如
/* @tainted */),配合类型检查工具追踪污染流
替代方案比修补更可靠
多数业务场景其实不需要真正的“动态导入”。优先考虑更安全的替代逻辑:
- 用策略对象或工厂函数代替按名加载模块
- 将行为配置化(JSON/YAML),通过固定模块解析配置并执行对应动作
- 前端路由级的模块拆分可交由构建工具(如 Webpack 的
import('./pages/' + page)在编译期静态分析)处理,而非运行时拼接











