🔒 修复分享列表接口的目录遍历漏洞
- 在 /api/share/:code/list 增加路径规范化与范围校验 - 使用 path.posix.normalize 处理 subPath,阻断 ../ 遍历攻击 - 调用 isPathWithinShare 验证请求路径在分享范围内 - 统一使用安全的 requestedPath 进行存储访问和直链生成 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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选择存储后端
|
// 使用统一存储接口,根据分享的storage_type选择存储后端
|
||||||
const { StorageInterface } = require('./storage');
|
const { StorageInterface } = require('./storage');
|
||||||
const storageType = share.storage_type || 'sftp';
|
const storageType = share.storage_type || 'sftp';
|
||||||
@@ -2957,14 +2971,13 @@ app.post('/api/share/:code/list', shareRateLimitMiddleware, async (req, res) =>
|
|||||||
}
|
}
|
||||||
// 如果是目录分享(分享所有文件)
|
// 如果是目录分享(分享所有文件)
|
||||||
else {
|
else {
|
||||||
const fullPath = subPath ? `${share.share_path}/${subPath}`.replace('//', '/') : share.share_path;
|
const list = await storage.list(requestedPath);
|
||||||
const list = await storage.list(fullPath);
|
|
||||||
|
|
||||||
formattedList = list.map(item => {
|
formattedList = list.map(item => {
|
||||||
// 构建完整的文件路径用于下载
|
// 构建完整的文件路径用于下载
|
||||||
let httpDownloadUrl = null;
|
let httpDownloadUrl = null;
|
||||||
if (storageType === 'sftp' && sanitizedShareHttpBase && item.type !== 'd') {
|
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}`;
|
const filePath = normalizedPath === '/' ? `/${item.name}` : `${normalizedPath}/${item.name}`;
|
||||||
httpDownloadUrl = buildHttpDownloadUrl(sanitizedShareHttpBase, filePath);
|
httpDownloadUrl = buildHttpDownloadUrl(sanitizedShareHttpBase, filePath);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user