Node.js(Koa框架)路径遍历漏洞修复技术咨询
问题描述
基于Koa框架的Node.js文件上传代码功能正常,但Veracode扫描指出const readStream = fs.createReadStream(file.filepath);存在路径遍历漏洞,需修复。
原代码如下:
const uploads = []; const files = ctx.request.files; for (let i = 0; i < Object.keys(files).length; i++) { const file = files[`file-${i}`]; if (file) uploads.push(file); } if (uploads.length == 0) { ctx.log.error(NO_FILES); ctx.status = 404; return ctx; } for (const element of uploads) { const file = element; const readStream = fs.createReadStream(file.filepath); const key = file.filepath.substring(file.filepath.lastIndexOf("/") + 1) + file.filepath.substring(file.filepath.lastIndexOf(".")); const params = { Body: readStream, Bucket: bucketName, ContentType: file.mimetype, Key: key, Metadata: { landParcelId }, }; let attach_url; try { const upload = s3.upload(params); const res = await upload.promise(); attach_url = res.Location; } catch (err) { ctx.log.error(err); ctx.status = 500; return ctx; } attachments.push({ landParcelId, description: file.originalFilename, fileName: file.originalFilename, typeId: ctx.request.body.documentType, fileType: file.mimetype, url: attach_url, }); } try { await landAttachment.bulkCreate(attachments); } catch (err) { ctx.log.error(err); ctx.status = 500; return ctx; } ctx.status = 200;
漏洞原因
直接信任file.filepath的值存在风险:若上传组件配置存在漏洞,或恶意请求篡改文件路径,可能导致代码读取服务器任意位置的文件,触发路径遍历攻击。
修复步骤
- 校验文件路径范围:确保待读取文件位于系统指定的临时上传目录内,拒绝超出范围的路径。
- 安全生成文件名:放弃从
filepath截取文件名,改用上传组件提供的originalFilename,并过滤特殊字符避免注入。 - 用内置工具处理路径:使用Node.js的
path模块解析路径,避免手动字符串处理出错。
修复后的代码
const path = require('path'); const os = require('os'); const uploads = []; const files = ctx.request.files; for (let i = 0; i < Object.keys(files).length; i++) { const file = files[`file-${i}`]; if (file) uploads.push(file); } if (uploads.length == 0) { ctx.log.error(NO_FILES); ctx.status = 404; return ctx; } // 定义允许的临时上传目录(根据实际上传组件配置调整,此处用系统默认临时目录) const allowedUploadDir = path.resolve(os.tmpdir()); for (const element of uploads) { const file = element; const resolvedFilePath = path.resolve(file.filepath); // 校验文件是否在允许的目录内,拦截路径遍历尝试 if (!resolvedFilePath.startsWith(allowedUploadDir)) { ctx.log.error('Invalid file path detected'); ctx.status = 400; return ctx; } const readStream = fs.createReadStream(resolvedFilePath); // 过滤文件名特殊字符,生成安全的S3存储Key const safeFileName = file.originalFilename.replace(/[^a-zA-Z0-9_\-.]/g, '_'); const key = `${landParcelId}_${safeFileName}`; const params = { Body: readStream, Bucket: bucketName, ContentType: file.mimetype, Key: key, Metadata: { landParcelId }, }; let attach_url; try { const upload = s3.upload(params); const res = await upload.promise(); attach_url = res.Location; } catch (err) { ctx.log.error(err); ctx.status = 500; return ctx; } attachments.push({ landParcelId, description: file.originalFilename, fileName: file.originalFilename, typeId: ctx.request.body.documentType, fileType: file.mimetype, url: attach_url, }); } try { await landAttachment.bulkCreate(attachments); } catch (err) { ctx.log.error(err); ctx.status = 500; return ctx; } ctx.status = 200;
额外注意事项
- 确保上传中间件(如koa-body)开启
strict: true配置,禁止上传超出临时目录的文件。 - 对
originalFilename的字符过滤可根据业务需求调整,但需覆盖常见特殊字符。 - S3 Key加入业务标识(如
landParcelId)可避免文件名重复导致的文件覆盖问题。
内容的提问来源于stack exchange,提问作者Partha Chowdhury
相关产品推荐
相关产品推荐

