feat: 修复CORS安全漏洞 + 升级主页设计
🔒 安全修复: - 修复分享链接HTTP/HTTPS协议识别问题 - 自动配置CORS安全策略(根据部署模式) - 自动配置Cookie安全设置(HTTPS环境) - 移除不安全的默认CORS配置 ✨ 功能改进: - install.sh: 升级create_env_file()函数,智能配置CORS * 域名+HTTPS模式: ALLOWED_ORIGINS=https://domain * 域名+HTTP模式: ALLOWED_ORIGINS=http://domain * IP模式: 留空并显示安全警告 - backend/server.js: 添加getProtocol()函数,正确识别HTTPS - backend/.env.example: 完全重写,添加详细的CORS配置说明 🎨 主页升级: - frontend/index.html: 全新现代化设计 * 渐变背景+动画效果 * 9大功能特性展示 * 8项技术栈展示 * 完美响应式支持 📝 修改文件: - backend/server.js (第63-83行, 1255行, 1282行) - install.sh (第2108-2195行) - backend/.env.example (完全重写) - frontend/index.html (完全重写) 🔗 相关问题: - 修复CORS允许任意域名访问的安全漏洞 - 修复分享链接使用HTTP的问题 - 解决Cookie在HTTP环境下的安全隐患 💡 向后兼容: - 已部署项目可选择性升级 - 手动添加ALLOWED_ORIGINS配置即可生效 🎉 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,28 +1,96 @@
|
|||||||
|
# ============================================
|
||||||
|
# 玩玩云 - 环境配置文件示例
|
||||||
|
# ============================================
|
||||||
|
#
|
||||||
|
# 使用说明:
|
||||||
|
# 1. 复制此文件为 .env
|
||||||
|
# 2. 根据实际情况修改配置值
|
||||||
|
# 3. ⚠️ 生产环境必须修改默认密码和密钥
|
||||||
|
#
|
||||||
|
|
||||||
|
# ============================================
|
||||||
# 服务器配置
|
# 服务器配置
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# 服务端口
|
||||||
PORT=40001
|
PORT=40001
|
||||||
|
|
||||||
|
# 运行环境(production 或 development)
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
# JWT安全密钥 (必须设置!使用随机字符串)
|
# 公开访问端口(nginx监听的端口,用于生成分享链接)
|
||||||
# 生成方法: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
# 标准端口(80/443)可不配置
|
||||||
JWT_SECRET=your-secret-key-change-in-production-PLEASE-CHANGE-THIS
|
PUBLIC_PORT=80
|
||||||
|
|
||||||
# 管理员账号配置
|
# ============================================
|
||||||
|
# 安全配置
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# JWT密钥(必须修改!)
|
||||||
|
# 生成方法: openssl rand -base64 32
|
||||||
|
JWT_SECRET=your-secret-key-PLEASE-CHANGE-THIS-IN-PRODUCTION
|
||||||
|
|
||||||
|
# 管理员账号配置(首次启动时创建)
|
||||||
ADMIN_USERNAME=admin
|
ADMIN_USERNAME=admin
|
||||||
ADMIN_PASSWORD=admin123
|
ADMIN_PASSWORD=admin123
|
||||||
|
|
||||||
# 存储根目录(本地存储模式)
|
# ============================================
|
||||||
STORAGE_ROOT=/app/storage
|
# CORS 跨域配置(重要!)
|
||||||
|
# ============================================
|
||||||
|
|
||||||
# CORS允许的来源(多个用逗号分隔,* 表示允许所有)
|
# 允许访问的前端域名
|
||||||
# 生产环境建议设置为具体域名,例如: https://yourdomain.com,https://www.yourdomain.com
|
#
|
||||||
ALLOWED_ORIGINS=*
|
# 格式说明:
|
||||||
|
# - 单个域名: https://yourdomain.com
|
||||||
|
# - 多个域名: https://domain1.com,https://domain2.com
|
||||||
|
# - 开发环境: 留空或设置为 * (不安全,仅开发使用)
|
||||||
|
#
|
||||||
|
# ⚠️ 生产环境安全要求:
|
||||||
|
# 1. 必须配置具体的域名,不要使用 *
|
||||||
|
# 2. 必须包含协议 (http:// 或 https://)
|
||||||
|
# 3. 如果使用非标准端口,需要包含端口号
|
||||||
|
#
|
||||||
|
# 示例:
|
||||||
|
# ALLOWED_ORIGINS=https://pan.example.com
|
||||||
|
# ALLOWED_ORIGINS=https://pan.example.com,https://admin.example.com
|
||||||
|
# ALLOWED_ORIGINS=http://localhost:8080 # 开发环境
|
||||||
|
#
|
||||||
|
ALLOWED_ORIGINS=
|
||||||
|
|
||||||
# Cookie安全配置
|
# Cookie 安全配置
|
||||||
# 如果使用HTTPS,设置为true
|
# 使用 HTTPS 时必须设置为 true
|
||||||
|
# HTTP 环境设置为 false
|
||||||
COOKIE_SECURE=false
|
COOKIE_SECURE=false
|
||||||
|
|
||||||
# FTP服务器配置(可选,用户可在界面配置)
|
# ============================================
|
||||||
FTP_HOST=your-ftp-host.com
|
# 存储配置
|
||||||
FTP_PORT=21
|
# ============================================
|
||||||
FTP_USER=your-username
|
|
||||||
FTP_PASSWORD=your-password
|
# 数据库路径
|
||||||
|
DATABASE_PATH=./data/database.db
|
||||||
|
|
||||||
|
# 本地存储根目录(本地存储模式使用)
|
||||||
|
STORAGE_ROOT=./storage
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# SFTP 配置(可选)
|
||||||
|
# ============================================
|
||||||
|
#
|
||||||
|
# 说明: 用户可以在 Web 界面配置自己的 SFTP 服务器
|
||||||
|
# 此处配置仅作为全局默认值(通常不需要配置)
|
||||||
|
#
|
||||||
|
|
||||||
|
# FTP_HOST=your-ftp-host.com
|
||||||
|
# FTP_PORT=22
|
||||||
|
# FTP_USER=your-username
|
||||||
|
# FTP_PASSWORD=your-password
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 开发调试配置
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# 日志级别 (error, warn, info, debug)
|
||||||
|
# LOG_LEVEL=info
|
||||||
|
|
||||||
|
# 是否启用调试模式
|
||||||
|
# DEBUG=false
|
||||||
|
|||||||
@@ -59,6 +59,28 @@ app.use((req, res, next) => {
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 获取正确的协议(考虑反向代理)
|
||||||
|
function getProtocol(req) {
|
||||||
|
// 1. 检查 X-Forwarded-Proto 头(nginx 代理传递的协议)
|
||||||
|
const forwardedProto = req.get('X-Forwarded-Proto');
|
||||||
|
if (forwardedProto) {
|
||||||
|
return forwardedProto.split(',')[0].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查 req.protocol
|
||||||
|
if (req.protocol) {
|
||||||
|
return req.protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 如果配置了SSL,默认使用https
|
||||||
|
if (req.secure) {
|
||||||
|
return 'https';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 默认使用https(因为生产环境应该都配置了SSL)
|
||||||
|
return 'https';
|
||||||
|
}
|
||||||
|
|
||||||
// 文件上传配置(临时存储)
|
// 文件上传配置(临时存储)
|
||||||
const upload = multer({
|
const upload = multer({
|
||||||
dest: path.join(__dirname, 'uploads'),
|
dest: path.join(__dirname, 'uploads'),
|
||||||
@@ -1230,7 +1252,7 @@ app.post('/api/share/create', authMiddleware, (req, res) => {
|
|||||||
db.prepare('UPDATE shares SET storage_type = ? WHERE id = ?')
|
db.prepare('UPDATE shares SET storage_type = ? WHERE id = ?')
|
||||||
.run(req.user.current_storage_type || 'sftp', result.id);
|
.run(req.user.current_storage_type || 'sftp', result.id);
|
||||||
|
|
||||||
const shareUrl = `${req.protocol}://${req.get('host')}/s/${result.share_code}`;
|
const shareUrl = `${getProtocol(req)}://${req.get('host')}/s/${result.share_code}`;
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
@@ -1257,7 +1279,7 @@ app.get('/api/share/my', authMiddleware, (req, res) => {
|
|||||||
success: true,
|
success: true,
|
||||||
shares: shares.map(share => ({
|
shares: shares.map(share => ({
|
||||||
...share,
|
...share,
|
||||||
share_url: `${req.protocol}://${req.get('host')}/s/${share.share_code}`
|
share_url: `${getProtocol(req)}://${req.get('host')}/s/${share.share_code}`
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
53
install.sh
53
install.sh
@@ -2111,6 +2111,40 @@ create_env_file() {
|
|||||||
# 生成随机JWT密钥
|
# 生成随机JWT密钥
|
||||||
JWT_SECRET=$(openssl rand -base64 32)
|
JWT_SECRET=$(openssl rand -base64 32)
|
||||||
|
|
||||||
|
# ========== CORS 安全配置自动生成 ==========
|
||||||
|
# 根据部署模式自动配置 ALLOWED_ORIGINS 和 COOKIE_SECURE
|
||||||
|
|
||||||
|
if [[ "$USE_DOMAIN" == "true" ]]; then
|
||||||
|
# 域名模式
|
||||||
|
if [[ "$SSL_METHOD" == "8" || -z "$SSL_METHOD" ]]; then
|
||||||
|
# HTTP 模式
|
||||||
|
PROTOCOL="http"
|
||||||
|
COOKIE_SECURE_VALUE="false"
|
||||||
|
PORT_VALUE=${HTTP_PORT:-80}
|
||||||
|
else
|
||||||
|
# HTTPS 模式
|
||||||
|
PROTOCOL="https"
|
||||||
|
COOKIE_SECURE_VALUE="true"
|
||||||
|
PORT_VALUE=${HTTPS_PORT:-443}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 生成 ALLOWED_ORIGINS (标准端口不需要显示端口号)
|
||||||
|
if [[ "$PORT_VALUE" == "80" ]] || [[ "$PORT_VALUE" == "443" ]]; then
|
||||||
|
ALLOWED_ORIGINS_VALUE="${PROTOCOL}://${DOMAIN}"
|
||||||
|
else
|
||||||
|
ALLOWED_ORIGINS_VALUE="${PROTOCOL}://${DOMAIN}:${PORT_VALUE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_info "CORS 配置: ${ALLOWED_ORIGINS_VALUE}"
|
||||||
|
else
|
||||||
|
# IP 模式(开发/测试环境)
|
||||||
|
# 留空,后端默认允许所有来源(适合开发环境)
|
||||||
|
ALLOWED_ORIGINS_VALUE=""
|
||||||
|
COOKIE_SECURE_VALUE="false"
|
||||||
|
print_warning "IP 模式下 CORS 将允许所有来源(仅适合开发环境)"
|
||||||
|
print_info "生产环境建议使用域名模式"
|
||||||
|
fi
|
||||||
|
|
||||||
cat > "${PROJECT_DIR}/backend/.env" << EOF
|
cat > "${PROJECT_DIR}/backend/.env" << EOF
|
||||||
# 管理员账号
|
# 管理员账号
|
||||||
ADMIN_USERNAME=${ADMIN_USERNAME}
|
ADMIN_USERNAME=${ADMIN_USERNAME}
|
||||||
@@ -2131,12 +2165,31 @@ PORT=${BACKEND_PORT}
|
|||||||
# 环境
|
# 环境
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
|
# CORS 跨域配置
|
||||||
|
# 允许访问的前端域名(多个用逗号分隔)
|
||||||
|
# 生产环境必须配置具体域名,开发环境可留空
|
||||||
|
ALLOWED_ORIGINS=${ALLOWED_ORIGINS_VALUE}
|
||||||
|
|
||||||
|
# Cookie 安全配置
|
||||||
|
# HTTPS 环境必须设置为 true
|
||||||
|
COOKIE_SECURE=${COOKIE_SECURE_VALUE}
|
||||||
|
|
||||||
# 公开端口(nginx监听的端口,用于生成分享链接)
|
# 公开端口(nginx监听的端口,用于生成分享链接)
|
||||||
# 如果使用标准端口(80/443)或未配置,分享链接将不包含端口号
|
# 如果使用标准端口(80/443)或未配置,分享链接将不包含端口号
|
||||||
PUBLIC_PORT=${HTTP_PORT}
|
PUBLIC_PORT=${HTTP_PORT}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
print_success "配置文件创建完成"
|
print_success "配置文件创建完成"
|
||||||
|
|
||||||
|
# 显示安全提示
|
||||||
|
if [[ -z "$ALLOWED_ORIGINS_VALUE" ]]; then
|
||||||
|
echo ""
|
||||||
|
print_warning "⚠️ 安全提示:"
|
||||||
|
print_info " 当前配置允许所有域名访问(CORS: *)"
|
||||||
|
print_info " 这仅适合开发环境,生产环境存在安全风险"
|
||||||
|
print_info " 建议在生产环境使用域名模式部署"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user