🔒 修复中高危安全漏洞并增强文件安全验证
**关键安全修复:** 1. 修复弱随机数生成器(中危) - 分享码生成从Math.random()改为crypto.randomBytes() - 防止分享链接被预测或暴力猜测 2. 增强文件上传安全验证(中危) - 新增isSafePathSegment()函数验证文件名 - 禁止路径遍历字符(..、/、\、控制字符) - 添加上传路径规范化和安全检查 **功能改进:** - 管理员界面显示用户邮箱验证状态 - 优化用户状态展示(已封禁/未激活/正常) **安全影响:** - 消除分享链接可预测性风险 - 防止文件上传路径遍历攻击 - 提升文件系统访问控制安全性 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
const Database = require('better-sqlite3');
|
const Database = require('better-sqlite3');
|
||||||
const bcrypt = require('bcryptjs');
|
const bcrypt = require('bcryptjs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
// 创建或连接数据库
|
// 创建或连接数据库
|
||||||
const db = new Database(path.join(__dirname, 'ftp-manager.db'));
|
const db = new Database(path.join(__dirname, 'ftp-manager.db'));
|
||||||
@@ -297,9 +298,10 @@ const ShareDB = {
|
|||||||
// 生成随机分享码
|
// 生成随机分享码
|
||||||
generateShareCode(length = 8) {
|
generateShareCode(length = 8) {
|
||||||
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||||
|
const bytes = crypto.randomBytes(length);
|
||||||
let code = '';
|
let code = '';
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
code += chars.charAt(Math.floor(Math.random() * chars.length));
|
code += chars[bytes[i] % chars.length];
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -197,6 +197,17 @@ function buildHttpDownloadUrl(rawBaseUrl, filePath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 校验文件名/路径片段安全(禁止分隔符、控制字符、..)
|
||||||
|
function isSafePathSegment(name) {
|
||||||
|
return (
|
||||||
|
typeof name === 'string' &&
|
||||||
|
name.length > 0 &&
|
||||||
|
!name.includes('..') &&
|
||||||
|
!/[/\\]/.test(name) &&
|
||||||
|
!/[\x00-\x1F]/.test(name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// 应用XSS过滤到所有POST/PUT请求的body
|
// 应用XSS过滤到所有POST/PUT请求的body
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
if ((req.method === 'POST' || req.method === 'PUT') && req.body) {
|
if ((req.method === 'POST' || req.method === 'PUT') && req.body) {
|
||||||
@@ -1986,9 +1997,27 @@ app.post('/api/upload', authMiddleware, upload.single('file'), async (req, res)
|
|||||||
const remotePath = req.body.path || '/';
|
const remotePath = req.body.path || '/';
|
||||||
// 修复中文文件名:multer将UTF-8转为了Latin1,需要转回来
|
// 修复中文文件名:multer将UTF-8转为了Latin1,需要转回来
|
||||||
const originalFilename = Buffer.from(req.file.originalname, 'latin1').toString('utf8');
|
const originalFilename = Buffer.from(req.file.originalname, 'latin1').toString('utf8');
|
||||||
const remoteFilePath = remotePath === '/'
|
// 文件名安全校验
|
||||||
|
if (!isSafePathSegment(originalFilename)) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: '文件名包含非法字符'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 路径安全校验
|
||||||
|
const normalizedPath = path.posix.normalize(remotePath || '/');
|
||||||
|
if (normalizedPath.includes('..')) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: '上传路径非法'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const safePath = normalizedPath === '.' ? '/' : normalizedPath;
|
||||||
|
|
||||||
|
const remoteFilePath = safePath === '/'
|
||||||
? `/${originalFilename}`
|
? `/${originalFilename}`
|
||||||
: `${remotePath}/${originalFilename}`;
|
: `${safePath}/${originalFilename}`;
|
||||||
|
|
||||||
let storage;
|
let storage;
|
||||||
|
|
||||||
@@ -3123,6 +3152,7 @@ app.get('/api/admin/users', authMiddleware, adminMiddleware, (req, res) => {
|
|||||||
email: u.email,
|
email: u.email,
|
||||||
is_admin: u.is_admin,
|
is_admin: u.is_admin,
|
||||||
is_active: u.is_active,
|
is_active: u.is_active,
|
||||||
|
is_verified: u.is_verified,
|
||||||
is_banned: u.is_banned,
|
is_banned: u.is_banned,
|
||||||
has_ftp_config: u.has_ftp_config,
|
has_ftp_config: u.has_ftp_config,
|
||||||
created_at: u.created_at,
|
created_at: u.created_at,
|
||||||
|
|||||||
@@ -1733,6 +1733,7 @@
|
|||||||
</td>
|
</td>
|
||||||
<td style="padding: 10px; text-align: center;">
|
<td style="padding: 10px; text-align: center;">
|
||||||
<span v-if="u.is_banned" style="color: #dc3545; font-weight: 600;">已封禁</span>
|
<span v-if="u.is_banned" style="color: #dc3545; font-weight: 600;">已封禁</span>
|
||||||
|
<span v-else-if="!u.is_verified" style="color: #fd7e14; font-weight: 600;">未激活</span>
|
||||||
<span v-else style="color: #28a745;">正常</span>
|
<span v-else style="color: #28a745;">正常</span>
|
||||||
</td>
|
</td>
|
||||||
<td style="padding: 10px; text-align: center;">
|
<td style="padding: 10px; text-align: center;">
|
||||||
|
|||||||
Reference in New Issue
Block a user