mirror of
https://github.com/Silentely/eSIM-Tools.git
synced 2026-09-03 06:24:20 +08:00
feat(security): 添加内容安全策略 (CSP)
- 在 HTML 头部加入 Content-Security-Policy 元标签 - 配置了各种资源的加载来源,增强了页面的安全性 - 包括脚本、样式、字体、图像等资源的加载限制
This commit is contained in:
162
SECURITY.md
Normal file
162
SECURITY.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# 安全指南
|
||||
|
||||
## 概述
|
||||
|
||||
本项目实施了多层安全措施来保护用户数据和系统安全。
|
||||
|
||||
## 🔒 安全措施
|
||||
|
||||
### 1. 依赖包安全
|
||||
|
||||
#### 定期更新
|
||||
- 使用 `npm update` 定期更新所有依赖包
|
||||
- 使用 `npm audit` 检查安全漏洞
|
||||
- 使用 `npm run security-check` 运行自定义安全检查
|
||||
|
||||
#### 已知漏洞修复
|
||||
- ✅ tar-fs: 路径遍历漏洞已修复
|
||||
- ✅ got: UNIX socket重定向漏洞已修复
|
||||
- ✅ ipx: 路径遍历绕过漏洞已修复
|
||||
- ✅ http-proxy-middleware: writeBody重复调用漏洞已修复
|
||||
- ✅ esbuild: 开发服务器安全问题已修复
|
||||
- ✅ on-headers: HTTP响应头操作漏洞已修复
|
||||
|
||||
### 2. 服务器安全
|
||||
|
||||
#### Helmet安全头
|
||||
```javascript
|
||||
const helmet = require('helmet');
|
||||
app.use(helmet());
|
||||
```
|
||||
|
||||
#### CORS配置
|
||||
```javascript
|
||||
const cors = require('cors');
|
||||
app.use(cors({
|
||||
origin: ['https://esim.cosr.eu.org', 'http://localhost:3000'],
|
||||
credentials: true
|
||||
}));
|
||||
```
|
||||
|
||||
### 3. 内容安全策略 (CSP)
|
||||
|
||||
所有HTML文件都配置了严格的CSP策略:
|
||||
|
||||
```html
|
||||
<meta http-equiv="Content-Security-Policy" content="
|
||||
default-src 'self';
|
||||
script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com;
|
||||
style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com;
|
||||
font-src 'self' https://cdnjs.cloudflare.com;
|
||||
connect-src 'self' https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com;
|
||||
img-src 'self' data: https:;
|
||||
frame-src 'none';
|
||||
">
|
||||
```
|
||||
|
||||
### 4. 数据安全
|
||||
|
||||
#### 本地存储
|
||||
- 敏感数据不存储在服务器上
|
||||
- 使用LocalStorage进行会话持久化
|
||||
- 2小时自动过期机制
|
||||
|
||||
#### API安全
|
||||
- 所有API请求通过Netlify Functions代理
|
||||
- 不直接暴露用户凭据
|
||||
- 使用HTTPS进行所有通信
|
||||
|
||||
### 5. 部署安全
|
||||
|
||||
#### HTTPS强制
|
||||
- 所有生产环境强制使用HTTPS
|
||||
- 自动重定向HTTP到HTTPS
|
||||
|
||||
#### 环境变量
|
||||
- 敏感配置使用环境变量
|
||||
- 不在代码中硬编码密钥
|
||||
|
||||
## 🛡️ 安全最佳实践
|
||||
|
||||
### 开发环境
|
||||
1. **定期更新依赖**
|
||||
```bash
|
||||
npm update
|
||||
npm audit fix
|
||||
```
|
||||
|
||||
2. **运行安全检查**
|
||||
```bash
|
||||
npm run security-check
|
||||
```
|
||||
|
||||
3. **代码审查**
|
||||
- 检查第三方库的使用
|
||||
- 验证API调用的安全性
|
||||
- 确保没有硬编码的敏感信息
|
||||
|
||||
### 生产环境
|
||||
1. **HTTPS部署**
|
||||
- 使用有效的SSL证书
|
||||
- 配置HSTS头
|
||||
|
||||
2. **监控和日志**
|
||||
- 监控异常访问模式
|
||||
- 记录安全相关事件
|
||||
|
||||
3. **定期安全审计**
|
||||
- 使用自动化工具检查漏洞
|
||||
- 定期审查访问日志
|
||||
|
||||
## 🚨 安全响应
|
||||
|
||||
### 发现漏洞时
|
||||
1. 立即评估漏洞严重程度
|
||||
2. 在24小时内发布修复
|
||||
3. 通知相关用户
|
||||
4. 更新安全文档
|
||||
|
||||
### 报告安全问题
|
||||
- 通过GitHub Issues报告
|
||||
- 提供详细的复现步骤
|
||||
- 包含环境信息
|
||||
|
||||
## 📋 安全检查清单
|
||||
|
||||
### 开发前
|
||||
- [ ] 运行 `npm audit`
|
||||
- [ ] 检查依赖包版本
|
||||
- [ ] 验证CSP配置
|
||||
|
||||
### 部署前
|
||||
- [ ] 运行 `npm run security-check`
|
||||
- [ ] 验证HTTPS配置
|
||||
- [ ] 检查环境变量
|
||||
|
||||
### 定期检查
|
||||
- [ ] 更新依赖包
|
||||
- [ ] 审查访问日志
|
||||
- [ ] 检查安全配置
|
||||
|
||||
## 🔧 安全工具
|
||||
|
||||
### 内置工具
|
||||
- `npm run security-check`: 自定义安全检查
|
||||
- `npm audit`: npm安全审计
|
||||
- `npm update`: 更新依赖包
|
||||
|
||||
### 推荐工具
|
||||
- [Snyk](https://snyk.io/): 依赖漏洞扫描
|
||||
- [OWASP ZAP](https://owasp.org/www-project-zap/): Web应用安全测试
|
||||
- [Security Headers](https://securityheaders.com/): 安全头检查
|
||||
|
||||
## 📞 安全联系
|
||||
|
||||
如果您发现安全问题,请:
|
||||
1. 通过GitHub Issues报告
|
||||
2. 提供详细的描述和复现步骤
|
||||
3. 不要公开披露,等待修复
|
||||
|
||||
## 免责声明
|
||||
|
||||
本安全指南提供了基本的安全措施,但不能保证100%的安全性。建议根据具体需求进行额外的安全评估。
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; connect-src 'self' https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com; img-src 'self' data: https:; frame-src 'none';">
|
||||
<title>eSIM工具 - Giffgaff & Simyo</title>
|
||||
|
||||
<!-- 性能优化:预连接与预加载关键资源 -->
|
||||
|
||||
1378
package-lock.json
generated
1378
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,8 @@
|
||||
"netlify-dev": "netlify dev",
|
||||
"deploy": "npm run build && netlify deploy --prod",
|
||||
"optimize-images": "node scripts/optimize-images.js",
|
||||
"compress": "node scripts/compress.js"
|
||||
"compress": "node scripts/compress.js",
|
||||
"security-check": "node scripts/security-check.js"
|
||||
},
|
||||
"keywords": [
|
||||
"esim",
|
||||
@@ -47,12 +48,16 @@
|
||||
"core-js": "^3.45.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"cssnano": "^6.0.1",
|
||||
"esbuild": "^0.25.8",
|
||||
"got": "^14.4.7",
|
||||
"http-proxy-middleware": "^3.0.5",
|
||||
"imagemin": "^8.0.1",
|
||||
"imagemin-mozjpeg": "^10.0.0",
|
||||
"imagemin-pngquant": "^9.0.2",
|
||||
"imagemin-webp": "^8.0.0",
|
||||
"netlify-cli": "^17.10.1",
|
||||
"nodemon": "^3.0.2",
|
||||
"on-headers": "^1.1.0",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-loader": "^7.3.3",
|
||||
|
||||
178
scripts/security-check.js
Normal file
178
scripts/security-check.js
Normal file
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// 已知的安全漏洞列表
|
||||
const knownVulnerabilities = {
|
||||
'tar-fs': {
|
||||
versions: ['<2.1.1', '<3.0.4'],
|
||||
description: 'Path traversal vulnerability',
|
||||
severity: 'High',
|
||||
fix: 'Update to latest version'
|
||||
},
|
||||
'got': {
|
||||
versions: ['<12.0.0'],
|
||||
description: 'Redirect to UNIX socket vulnerability',
|
||||
severity: 'Moderate',
|
||||
fix: 'Update to version 12.0.0 or later'
|
||||
},
|
||||
'ipx': {
|
||||
versions: ['<2.1.0'],
|
||||
description: 'Path traversal via prefix matching bypass',
|
||||
severity: 'Moderate',
|
||||
fix: 'Update to version 2.1.0 or later'
|
||||
},
|
||||
'http-proxy-middleware': {
|
||||
versions: ['<2.0.7'],
|
||||
description: 'writeBody called twice vulnerability',
|
||||
severity: 'Moderate',
|
||||
fix: 'Update to version 2.0.7 or later'
|
||||
},
|
||||
'esbuild': {
|
||||
versions: ['<0.19.0'],
|
||||
description: 'Development server security issue',
|
||||
severity: 'Moderate',
|
||||
fix: 'Update to version 0.19.0 or later'
|
||||
},
|
||||
'on-headers': {
|
||||
versions: ['<1.1.0'],
|
||||
description: 'HTTP response header manipulation',
|
||||
severity: 'Low',
|
||||
fix: 'Update to version 1.1.0 or later'
|
||||
}
|
||||
};
|
||||
|
||||
// 检查版本是否在漏洞范围内
|
||||
function isVulnerable(version, vulnerableVersions) {
|
||||
const semver = require('semver');
|
||||
return vulnerableVersions.some(range => semver.satisfies(version, range));
|
||||
}
|
||||
|
||||
// 解析package-lock.json
|
||||
function parsePackageLock() {
|
||||
try {
|
||||
const packageLockPath = path.join(__dirname, '../package-lock.json');
|
||||
const packageLock = JSON.parse(fs.readFileSync(packageLockPath, 'utf8'));
|
||||
return packageLock.dependencies || {};
|
||||
} catch (error) {
|
||||
console.error('Error reading package-lock.json:', error.message);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// 检查依赖包的安全状态
|
||||
function checkDependencies() {
|
||||
const dependencies = parsePackageLock();
|
||||
const vulnerabilities = [];
|
||||
|
||||
Object.keys(dependencies).forEach(pkgName => {
|
||||
const pkg = dependencies[pkgName];
|
||||
const version = pkg.version;
|
||||
|
||||
if (knownVulnerabilities[pkgName]) {
|
||||
const vuln = knownVulnerabilities[pkgName];
|
||||
if (isVulnerable(version, vuln.versions)) {
|
||||
vulnerabilities.push({
|
||||
package: pkgName,
|
||||
version: version,
|
||||
description: vuln.description,
|
||||
severity: vuln.severity,
|
||||
fix: vuln.fix
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return vulnerabilities;
|
||||
}
|
||||
|
||||
// 生成安全报告
|
||||
function generateSecurityReport() {
|
||||
console.log('🔒 安全检查报告\n');
|
||||
|
||||
const vulnerabilities = checkDependencies();
|
||||
|
||||
if (vulnerabilities.length === 0) {
|
||||
console.log('✅ 未发现已知的安全漏洞');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`⚠️ 发现 ${vulnerabilities.length} 个潜在安全漏洞:\n`);
|
||||
|
||||
vulnerabilities.forEach((vuln, index) => {
|
||||
console.log(`${index + 1}. ${vuln.package}@${vuln.version}`);
|
||||
console.log(` 严重程度: ${vuln.severity}`);
|
||||
console.log(` 描述: ${vuln.description}`);
|
||||
console.log(` 修复建议: ${vuln.fix}\n`);
|
||||
});
|
||||
|
||||
console.log('🔧 修复建议:');
|
||||
console.log('1. 运行 npm update 更新所有依赖');
|
||||
console.log('2. 运行 npm audit fix 自动修复');
|
||||
console.log('3. 手动更新特定包到最新版本');
|
||||
}
|
||||
|
||||
// 检查开发环境安全配置
|
||||
function checkSecurityConfig() {
|
||||
console.log('\n🔧 安全配置检查:\n');
|
||||
|
||||
// 检查Helmet配置
|
||||
const serverPath = path.join(__dirname, '../server.js');
|
||||
if (fs.existsSync(serverPath)) {
|
||||
const serverContent = fs.readFileSync(serverPath, 'utf8');
|
||||
if (serverContent.includes('helmet')) {
|
||||
console.log('✅ Helmet安全头已配置');
|
||||
} else {
|
||||
console.log('⚠️ 建议添加Helmet安全头');
|
||||
}
|
||||
}
|
||||
|
||||
// 检查CORS配置
|
||||
if (fs.existsSync(serverPath)) {
|
||||
const serverContent = fs.readFileSync(serverPath, 'utf8');
|
||||
if (serverContent.includes('cors')) {
|
||||
console.log('✅ CORS配置已设置');
|
||||
} else {
|
||||
console.log('⚠️ 建议配置CORS');
|
||||
}
|
||||
}
|
||||
|
||||
// 检查Content Security Policy
|
||||
const htmlFiles = [
|
||||
'index.html',
|
||||
'src/giffgaff/giffgaff_complete_esim.html',
|
||||
'src/simyo/simyo_complete_esim.html'
|
||||
];
|
||||
|
||||
htmlFiles.forEach(file => {
|
||||
const filePath = path.join(__dirname, '..', file);
|
||||
if (fs.existsSync(filePath)) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
if (content.includes('Content-Security-Policy')) {
|
||||
console.log(`✅ ${file} 已配置CSP`);
|
||||
} else {
|
||||
console.log(`⚠️ ${file} 建议添加CSP配置`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 主函数
|
||||
function main() {
|
||||
generateSecurityReport();
|
||||
checkSecurityConfig();
|
||||
|
||||
console.log('\n📋 安全最佳实践:');
|
||||
console.log('1. 定期更新依赖包');
|
||||
console.log('2. 使用npm audit检查安全漏洞');
|
||||
console.log('3. 配置适当的安全头');
|
||||
console.log('4. 实施内容安全策略(CSP)');
|
||||
console.log('5. 使用HTTPS部署');
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
||||
module.exports = { checkDependencies, generateSecurityReport };
|
||||
@@ -5,6 +5,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="color-scheme" content="light dark" />
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; connect-src 'self' https://api.qrserver.com https://appapi.simyo.nl https://api.giffgaff.com https://id.giffgaff.com https://publicapi.giffgaff.com; img-src 'self' data: https:; frame-src 'none';">
|
||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
||||
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
|
||||
<title>Simyo NL eSIM 申请工具</title>
|
||||
|
||||
Reference in New Issue
Block a user