fix: 修复分享过期时间和防爆破保护覆盖不全的安全问题
问题1 - 分享过期时间未强制校验:
- 在ShareDB.findByCode()中添加过期时间检查
- SQL条件: AND (s.expires_at IS NULL OR s.expires_at > datetime('now'))
- 现在过期的分享链接将返回404,无法访问
问题2 - 分享密码防爆破保护覆盖不全:
- 给/api/share/:code/list添加shareRateLimitMiddleware
- 给/api/share/:code/download-file添加shareRateLimitMiddleware
- 在两个接口的密码验证失败时调用recordFailure
- 在两个接口的密码验证成功时调用recordSuccess
- 防止攻击者绕过/verify接口直接暴力破解
影响:
- 分享过期后将无法访问(安全性提升)
- 所有分享密码验证接口都受到限流保护(10次/10分钟)
- 修复了可绕过防爆破保护的安全漏洞
This commit is contained in:
@@ -1735,7 +1735,7 @@ app.post('/api/share/:code/verify', shareRateLimitMiddleware, async (req, res) =
|
||||
});
|
||||
|
||||
// 获取分享的文件列表(支持本地存储和SFTP)
|
||||
app.post('/api/share/:code/list', async (req, res) => {
|
||||
app.post('/api/share/:code/list', shareRateLimitMiddleware, async (req, res) => {
|
||||
const { code } = req.params;
|
||||
const { password, path: subPath } = req.body;
|
||||
|
||||
@@ -1753,12 +1753,21 @@ app.post('/api/share/:code/list', async (req, res) => {
|
||||
|
||||
// 验证密码
|
||||
if (share.share_password && !ShareDB.verifyPassword(password, share.share_password)) {
|
||||
// 记录密码错误
|
||||
if (req.shareRateLimitKey) {
|
||||
shareLimiter.recordFailure(req.shareRateLimitKey);
|
||||
}
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '密码错误'
|
||||
});
|
||||
}
|
||||
|
||||
// 清除失败记录(密码验证成功或无密码)
|
||||
if (req.shareRateLimitKey && share.share_password) {
|
||||
shareLimiter.recordSuccess(req.shareRateLimitKey);
|
||||
}
|
||||
|
||||
// 获取分享者的用户信息
|
||||
const shareOwner = UserDB.findById(share.user_id);
|
||||
if (!shareOwner) {
|
||||
@@ -1912,7 +1921,7 @@ app.post('/api/share/:code/download', (req, res) => {
|
||||
});
|
||||
|
||||
// 分享文件下载(支持本地存储和SFTP,公开API,需要分享码和密码验证)
|
||||
app.get('/api/share/:code/download-file', async (req, res) => {
|
||||
app.get('/api/share/:code/download-file', shareRateLimitMiddleware, async (req, res) => {
|
||||
const { code } = req.params;
|
||||
const { path: filePath, password } = req.query;
|
||||
let storage;
|
||||
@@ -1936,11 +1945,20 @@ app.get('/api/share/:code/download-file', async (req, res) => {
|
||||
|
||||
// 验证密码(如果需要)
|
||||
if (share.share_password) {
|
||||
// 记录密码错误
|
||||
if (req.shareRateLimitKey) {
|
||||
shareLimiter.recordFailure(req.shareRateLimitKey);
|
||||
}
|
||||
if (!password || !ShareDB.verifyPassword(password, share.share_password)) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: '密码错误或未提供密码'
|
||||
});
|
||||
// 清除失败记录(密码验证成功)
|
||||
if (req.shareRateLimitKey && share.share_password) {
|
||||
shareLimiter.recordSuccess(req.shareRateLimitKey);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user