From 2fb23217508548ec04ad8b5c92922da01a1aa4dd Mon Sep 17 00:00:00 2001 From: yuyx <237899745@qq.com> Date: Tue, 25 Nov 2025 11:10:48 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20=E4=BF=AE=E5=A4=8D=E5=88=86?= =?UTF-8?q?=E4=BA=AB=E5=88=97=E8=A1=A8=E6=8E=A5=E5=8F=A3=E7=9A=84=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E9=81=8D=E5=8E=86=E6=BC=8F=E6=B4=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 /api/share/:code/list 增加路径规范化与范围校验 - 使用 path.posix.normalize 处理 subPath,阻断 ../ 遍历攻击 - 调用 isPathWithinShare 验证请求路径在分享范围内 - 统一使用安全的 requestedPath 进行存储访问和直链生成 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/server.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/backend/server.js b/backend/server.js index dc321d2..c378ebf 100644 --- a/backend/server.js +++ b/backend/server.js @@ -2902,6 +2902,20 @@ app.post('/api/share/:code/list', shareRateLimitMiddleware, async (req, res) => }); } + // 构造安全的请求路径,防止越权遍历 + const baseSharePath = (share.share_path || '/').replace(/\\/g, '/'); + const requestedPath = subPath + ? path.posix.normalize(`${baseSharePath}/${subPath}`) + : baseSharePath; + + // 校验请求路径是否在分享范围内 + if (!isPathWithinShare(requestedPath, share)) { + return res.status(403).json({ + success: false, + message: '无权访问该路径' + }); + } + // 使用统一存储接口,根据分享的storage_type选择存储后端 const { StorageInterface } = require('./storage'); const storageType = share.storage_type || 'sftp'; @@ -2957,14 +2971,13 @@ app.post('/api/share/:code/list', shareRateLimitMiddleware, async (req, res) => } // 如果是目录分享(分享所有文件) else { - const fullPath = subPath ? `${share.share_path}/${subPath}`.replace('//', '/') : share.share_path; - const list = await storage.list(fullPath); + const list = await storage.list(requestedPath); formattedList = list.map(item => { // 构建完整的文件路径用于下载 let httpDownloadUrl = null; if (storageType === 'sftp' && sanitizedShareHttpBase && item.type !== 'd') { - const normalizedPath = fullPath.startsWith('/') ? fullPath : `/${fullPath}`; + const normalizedPath = requestedPath.startsWith('/') ? requestedPath : `/${requestedPath}`; const filePath = normalizedPath === '/' ? `/${item.name}` : `${normalizedPath}/${item.name}`; httpDownloadUrl = buildHttpDownloadUrl(sanitizedShareHttpBase, filePath); }