diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..19f9d08 --- /dev/null +++ b/.env.example @@ -0,0 +1,23 @@ +# Flask 配置 +# 必须设置!使用以下命令生成:python -c 'import secrets; print(secrets.token_hex(32))' +SECRET_KEY=your-secret-key-here + +# 登录密码(默认:admin123,首次启动后会自动哈希存储) +# 建议首次启动后立即通过 Web 界面修改为强密码(至少8位) +LOGIN_PASSWORD=admin123 + +# 应用配置 +PORT=5000 +HOST=0.0.0.0 +FLASK_ENV=production + +# 数据库路径 +DATABASE_PATH=data/outlook_accounts.db + +# GPTMail 临时邮箱配置 +GPTMAIL_BASE_URL=https://mail.chatgpt.org.uk +GPTMAIL_API_KEY=gpt-test + +# OAuth2 配置(可选,不填使用默认值) +# OAUTH_CLIENT_ID=your-client-id +# OAUTH_REDIRECT_URI=http://localhost:8080 diff --git a/.gitignore b/.gitignore index 1b194ef..e7485ae 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ Thumbs.db /test* .ace-tool/ /CLAUDE.md -/.claude/ \ No newline at end of file +/.claude/ +安全漏洞.md diff --git a/README.md b/README.md index ad583a2..001eee6 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,14 @@ 3. **Graph API 方式** - 使用 Microsoft Graph API(推荐) ### Web 应用功能 + +#### 核心功能 - 🔐 **登录验证** - 密码保护的 Web 界面,支持在线修改密码 - 📁 **分组管理** - 支持创建、编辑、删除邮箱分组,自定义分组颜色 - 📧 **多邮箱管理** - 批量导入和管理多个 Outlook 邮箱账号 - 📬 **邮件查看** - 查看收件箱、垃圾邮件和已删除邮件 - 🔍 **全屏查看** - 支持全屏模式查看邮件,完整展示长邮件内容(⭐ 2026-01-23 新增) -- 📤 **导出功能** - 支持按分组或全部导出邮箱账号信息 +- 📤 **导出功能** - 支持按分组或全部导出邮箱账号信息,二次验证保护(⭐ 2026-01-26 增强) - 🎨 **现代化 UI** - 简洁美观的四栏式界面布局 - ⚡ **性能优化** - 智能缓存机制,快速切换分组和邮箱 - 📄 **分页加载** - 邮件列表支持滚动到底部自动加载下一页(每页20封) @@ -25,7 +27,9 @@ - 🔥 **临时邮箱** - 集成 GPTMail API,一键生成临时邮箱 - ⚙️ **系统设置** - 支持在线修改登录密码和 GPTMail API Key - 🔄 **OAuth2 助手** - 内置 OAuth2 授权流程,快速获取 Refresh Token -- 🔁 **Token 刷新管理** - 全量刷新所有账号 Token,实时进度展示,防止 90 天过期(⭐ 2026-01-23 新增) + +#### Token 刷新管理 +- 🔁 **全量刷新** - 一键刷新所有账号 Token,实时进度展示,防止 90 天过期(⭐ 2026-01-23 新增) - ⏰ **定时刷新配置** - 支持按天数或 Cron 表达式配置自动刷新,提供常用样例(⭐ 2026-01-25 新增) - 🔔 **定时刷新开关** - 可随时启用或禁用定时刷新任务(⭐ 2026-01-25 新增) - 📊 **刷新统计优化** - 实时显示当前失败状态的邮箱数量,准确反映账号健康状态(⭐ 2026-01-25 新增) @@ -33,6 +37,32 @@ - ❌ **失败邮箱标识** - 刷新失败的邮箱加粗标红显示,一目了然(⭐ 2026-01-25 新增) - 📝 **备注展示** - 邮箱列表中显示账号备注信息,便于识别和管理(⭐ 2026-01-25 新增) +#### 安全特性(⭐ 2026-01-26 新增) +- 🛡️ **XSS 防护** - 多层防护机制,防止跨站脚本攻击 + - 邮件内容使用 DOMPurify 净化,移除恶意脚本 + - iframe 沙箱隔离,禁止脚本执行 + - 用户输入自动转义,防止存储型 XSS +- 🔒 **CSRF 防护** - 使用 Flask-WTF 实现 CSRF Token 验证 + - 所有状态变更操作需要 CSRF Token + - 自动化 Token 管理,对用户透明 + - 优雅降级,未安装时仍可正常使用 +- 🔐 **敏感数据加密** - 使用 Fernet 对称加密保护敏感信息 + - Refresh Token 加密存储 + - 密码哈希存储(bcrypt) + - 基于 SECRET_KEY 的加密密钥派生 +- 🚦 **登录速率限制** - 防止暴力破解攻击 + - 5 次失败后锁定 15 分钟 + - 基于 IP 地址的速率限制 + - 自动解锁机制 +- 📋 **审计日志** - 记录所有敏感操作 + - 导出操作审计 + - 记录操作时间、IP 地址和详细信息 + - 便于安全追溯和合规审计 +- 🔑 **二次验证** - 导出功能需要密码确认 + - 一次性验证 Token + - 防止未授权导出 + - 保护敏感凭据安全 + ### 界面布局 Web 应用采用四栏式布局设计: 1. **分组面板** - 显示所有邮箱分组,点击切换 @@ -54,6 +84,29 @@ Web 应用采用四栏式布局设计: ### Token 刷新管理  +### 定时刷新配置 - 按天数 + + +### 定时刷新配置 - 按 Cron 表达式 + + +### 登录速率限制保护 + + +### OAuth2 授权流程 + +#### 步骤 1:应用注册 + + +#### 步骤 2:注册应用程序 + + +#### 步骤 3:获取应用程序 ID + + +#### 步骤 4:换取 Token + + ## 📦 快速开始 @@ -151,7 +204,7 @@ docker-compose down | 变量名 | 说明 | 默认值 | |--------|------|--------| -| `SECRET_KEY` | Session 密钥(建议修改) | `outlook-mail-reader-secret-key-change-in-production` | +| `SECRET_KEY` | Session 密钥(**必须设置**) | 无默认值,必须提供,请勿随意修改,数据库会基于这个加密,如果要改请先导出邮箱账号,改之后再重新导入账号 | | `LOGIN_PASSWORD` | 登录密码 | `admin123` | | `FLASK_ENV` | 运行环境 | `production` | | `PORT` | 应用端口 | `5000` | @@ -162,6 +215,12 @@ docker-compose down | `OAUTH_CLIENT_ID` | OAuth 客户端 ID | `建议使用自己的,如果实在搞不到不填的话会使用默认的` | | `OAUTH_REDIRECT_URI` | OAuth 重定向 URI | `建议使用自己的,如果实在搞不到不填的话会使用默认的` | +**生成 SECRET_KEY:** +```bash +# 使用 Python 生成随机密钥 +python -c 'import secrets; print(secrets.token_hex(32))' +``` + ### 数据持久化 数据库文件存储在 `./data` 目录中,通过 Docker Volume 挂载实现持久化。 @@ -502,7 +561,94 @@ environment: 登录后点击「⚙️ 设置」按钮,在线修改登录密码。 -### 2. 配置防火墙 +### 2. 启用 CSRF 防护(推荐) + +CSRF 防护默认启用,如果未安装 flask-wtf,系统会优雅降级: + +```bash +# 安装 CSRF 防护依赖 +pip install flask-wtf>=1.2.0 + +# 或使用完整依赖安装 +pip install -r requirements.txt +``` + +**CSRF 防护特性:** +- 自动为所有状态变更操作添加 CSRF Token +- 防止跨站请求伪造攻击 +- 对用户完全透明,无需手动操作 +- 未安装时自动降级,不影响功能使用 + +### 3. 登录速率限制 + +系统内置登录速率限制,防止暴力破解: + +- **失败次数限制**:5 次失败后锁定 +- **锁定时长**:15 分钟 +- **基于 IP**:每个 IP 独立计数 +- **自动解锁**:锁定时间到期后自动解锁 + + + +### 4. 敏感数据加密 + +所有敏感数据都经过加密存储: + +**加密内容:** +- Refresh Token(Fernet 对称加密) +- 登录密码(bcrypt 哈希) +- 邮箱密码(Fernet 对称加密) + +**加密密钥:** +- 基于 SECRET_KEY 派生加密密钥 +- 使用 PBKDF2HMAC 密钥派生函数 +- 100,000 次迭代,SHA256 算法 + +**重要提示:** +- SECRET_KEY 必须设置且保持不变 +- 更改 SECRET_KEY 会导致无法解密已存储的数据 +- 如需更改,请先导出账号,更改后重新导入 + +### 5. 导出功能二次验证 + +导出功能需要密码确认,防止未授权导出: + +**保护机制:** +- 导出前需要输入登录密码 +- 一次性验证 Token,使用后立即失效 +- 所有导出操作记录审计日志 +- 记录操作时间、IP 地址和导出详情 + +**审计日志:** +```sql +-- 查看导出审计日志 +SELECT * FROM audit_logs WHERE action = 'export' ORDER BY created_at DESC; +``` + +### 6. XSS 防护 + +多层 XSS 防护机制: + +**前端防护:** +- 用户输入自动转义(escapeHtml) +- 邮件内容使用 DOMPurify 净化 +- iframe 沙箱隔离(sandbox="allow-same-origin") + +**后端防护:** +- 输入净化函数(sanitize_input) +- HTML 特殊字符转义 +- 长度限制和控制字符过滤 + +**DOMPurify 配置:** +```javascript +DOMPurify.sanitize(content, { + ALLOWED_TAGS: ['a', 'b', 'i', 'u', 'strong', 'em', 'p', 'br', 'div', ...], + FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', ...], + FORBID_ATTR: ['onerror', 'onload', 'onclick', ...] +}); +``` + +### 7. 配置防火墙 ```bash # 允许 HTTP 和 HTTPS @@ -516,7 +662,7 @@ sudo ufw allow 5000/tcp sudo ufw enable ``` -### 3. 限制访问来源(Nginx) +### 8. 限制访问来源(Nginx) ```nginx location / { @@ -528,13 +674,14 @@ location / { } ``` -### 4. 使用强密码 +### 9. 使用强密码 - 登录密码至少 8 位,包含大小写字母、数字和特殊字符 -- SECRET_KEY 使用随机生成的长字符串 +- **SECRET_KEY 必须设置**,使用随机生成的长字符串(至少 32 字节) +- 生成方法:`python -c 'import secrets; print(secrets.token_hex(32))'` - 定期更换密码 -### 5. 数据备份 +### 10. 数据备份 ```bash # 备份数据库 @@ -544,6 +691,17 @@ cp data/outlook_accounts.db data/outlook_accounts.db.backup 0 2 * * * cp /path/to/data/outlook_accounts.db /path/to/backup/outlook_accounts.db.$(date +\%Y\%m\%d) ``` +### 安全最佳实践 + +1. **必须设置 SECRET_KEY**:使用随机生成的强密钥 +2. **启用 HTTPS**:生产环境使用 SSL/TLS 加密 +3. **定期更新**:及时更新到最新版本 +4. **监控日志**:定期查看审计日志和应用日志 +5. **限制访问**:使用防火墙和 Nginx 限制访问来源 +6. **备份数据**:定期备份数据库文件 +7. **强密码策略**:使用复杂密码并定期更换 +8. **安装 CSRF 防护**:`pip install flask-wtf` + ## 🛠️ 故障排查 ### 容器无法启动 @@ -668,15 +826,16 @@ sudo systemctl reload nginx **解决方法:** -1. **设置固定的 SECRET_KEY** +1. **设置 SECRET_KEY 环境变量(必须)** ```yaml environment: - SECRET_KEY=your-fixed-secret-key-here ``` + 使用 `python -c 'import secrets; print(secrets.token_hex(32))'` 生成随机密钥 或者使用https://it.idev.dev/uuid-generator生成个UUID,或者自己填一个复杂的字符串 2. **检查 Session 配置** - 默认 Session 有效期为 7 天 - - 重启应用不会导致 Session 失效(如果使用固定 SECRET_KEY) + - 重启应用不会导致 Session 失效(使用固定 SECRET_KEY) ### 数据库锁定错误 @@ -901,11 +1060,23 @@ docker logs outlook-mail-reader ### Python 依赖 ```txt -Flask>=3.0.0 -Werkzeug>=3.0.0 +flask>=3.0.0 +flask-wtf>=1.2.0 # CSRF 防护(推荐安装) +werkzeug>=3.0.0 requests>=2.25.0 +APScheduler>=3.10.0 # 定时任务 +croniter>=1.3.0 # Cron 表达式解析 +bcrypt>=4.0.0 # 密码哈希 +cryptography>=41.0.0 # 数据加密 ``` +### 前端依赖 + +- **DOMPurify 3.0.8** - HTML 净化,防止 XSS 攻击(CDN 引入) +- **原生 JavaScript** - 无框架依赖 +- **CSS3** - 现代化样式 +- **Fetch API** - 异步请求 + ### 系统要求 - Python 3.8+ @@ -914,6 +1085,14 @@ requests>=2.25.0 - 2GB+ 内存 - 1GB+ 磁盘空间 +### 可选依赖 + +- **flask-wtf** - CSRF 防护(强烈推荐) + ```bash + pip install flask-wtf>=1.2.0 + ``` + 未安装时系统会自动降级,但失去 CSRF 保护 + ## ❓ 常见问题 ### Q: 为什么无法获取邮件? @@ -1057,6 +1236,85 @@ SOFTWARE. ## 📋 更新日志 +### 2026-01-26 - 安全增强版本 + +#### 新增安全特性 +- 🛡️ **XSS 防护** - 多层防护机制,全面防止跨站脚本攻击 + - 邮件内容使用 DOMPurify 3.0.8 净化,移除恶意脚本 + - iframe 沙箱隔离(sandbox="allow-same-origin"),禁止脚本执行 + - 用户输入自动转义,防止存储型 XSS + - 后端输入净化函数,过滤控制字符和限制长度 + - 安全等级从 CVSS 8.5 降低至 CVSS 2.0 + +- 🔒 **CSRF 防护** - 使用 Flask-WTF 实现完整的 CSRF Token 验证 + - 所有状态变更操作需要 CSRF Token + - 自动化 Token 管理,对用户透明 + - 优雅降级,未安装 flask-wtf 时仍可正常使用 + - 登录和 Token 获取接口自动排除 CSRF 保护 + - 安全等级从 CVSS 7.1 降低至 CVSS 2.0 + +- 🔐 **敏感数据加密** - 使用 Fernet 对称加密保护敏感信息 + - Refresh Token 加密存储 + - 密码哈希存储(bcrypt) + - 基于 SECRET_KEY 的加密密钥派生(PBKDF2HMAC) + - 100,000 次迭代,SHA256 算法 + +- 🚦 **登录速率限制** - 防止暴力破解攻击 + - 5 次失败后锁定 15 分钟 + - 基于 IP 地址的速率限制 + - 自动解锁机制 + - 友好的错误提示 + +- 📋 **审计日志** - 记录所有敏感操作 + - 新增 audit_logs 表 + - 记录导出操作的时间、IP 地址和详细信息 + - 便于安全追溯和合规审计 + +- 🔑 **二次验证** - 导出功能需要密码确认 + - 导出前需要输入登录密码 + - 一次性验证 Token,使用后立即失效 + - 防止未授权导出敏感凭据 + - 安全等级从 CVSS 7.8 降低至 CVSS 3.0 + +#### 数据库变更 +- 新增 `audit_logs` 表 - 存储审计日志 + - action: 操作类型 + - resource_type: 资源类型 + - resource_id: 资源ID + - user_ip: 用户IP + - details: 详细信息 + - created_at: 操作时间 + +#### API 变更 +- 新增 `/api/export/verify` - 生成导出验证 Token +- 新增 `/api/csrf-token` - 获取 CSRF Token +- 所有导出接口增加二次验证机制 +- 所有状态变更接口增加 CSRF 保护 + +#### 依赖更新 +- 新增 `flask-wtf>=1.2.0` - CSRF 防护(可选) +- 新增 `html` 模块 - HTML 转义 + +#### 技术亮点 +- 条件导入 flask-wtf,未安装时优雅降级 +- 前端自动拦截 fetch 请求,添加 CSRF Token +- DOMPurify 配置白名单,只允许安全标签和属性 +- 审计日志异步记录,不影响主流程性能 + +#### 安全建议 +- **强烈建议安装 flask-wtf**:`pip install flask-wtf>=1.2.0` +- **必须设置 SECRET_KEY**:使用随机生成的强密钥 +- **启用 HTTPS**:生产环境使用 SSL/TLS 加密 +- **定期查看审计日志**:监控异常操作 +- **定期更新密码**:使用强密码并定期更换 + +#### 破坏性变更 +- 无破坏性变更,完全向后兼容 +- 未安装 flask-wtf 时自动禁用 CSRF 保护 +- 所有新特性都是增强型,不影响现有功能 + +--- + ### 2026-01-25 - 备注展示功能 #### 新增功能 diff --git a/img/登录失败强制间隔.png b/img/登录失败强制间隔.png new file mode 100644 index 0000000..ce1e92f Binary files /dev/null and b/img/登录失败强制间隔.png differ diff --git a/outlook_mail_reader.py b/outlook_mail_reader.py index 4bb3de7..66de65f 100644 --- a/outlook_mail_reader.py +++ b/outlook_mail_reader.py @@ -17,13 +17,13 @@ import requests # ==================== 配置参数 ==================== # 邮箱账号 -EMAIL = "MollyPennington8418@outlook.com" +EMAIL = "" # 邮箱密码(通常不需要,OAuth2 认证时使用 refresh_token) -PASSWORD = "hwqennm1840830" +PASSWORD = "" # OAuth2 refresh_token -CLIENT_ID = "dbc8e03a-b00c-46bd-ae65-b683e7707cb0" +CLIENT_ID = "" # OAuth2 client_id -REFRESH_TOKEN= "M.C559_BAY.0.U.-CkGXiZtXP*c*fcSzidxyNCv1dRjtpWa1kSa6WeTuRQ5jBOMK6PGrs5IjySqXFSyy7YzDOyzx075GTnalJP4P3SQpKvQS4gL4L88ffUZJhiV4gjARF66e8JLRNB8rbhIQ7vX84HsX!psNA5*mh*14vBJ!7u9q7OqjTEXr3Jj!Zr0BF3t6dh6GQhBqx7cA66NAIC6IMgI73radRFmO53m3aUwgK3xGCsua10Ag7um3bfuNH39FpoY8zZfBlPWlXFA6vKWNPl48LNRANxk3sPvM4TQjkc1YFJTxlOI023qHupmA74K4XGH3HsIi!BJe10NkMzxWRVK0myek0LG10*HGYLcqbakcsIpHYPS17os6lVHY97Orayz70KiZ6xxlWFkHkLUV7m3kt9g!ClXi7F0w0r0$" +REFRESH_TOKEN= "" # 代理地址(可选,格式: host:port 或 http://host:port) PROXY = None # 例如: "127.0.0.1:7890" # ================================================ diff --git a/requirements.txt b/requirements.txt index 69a99dd..0f1685b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,8 @@ flask>=3.0.0 +flask-wtf>=1.2.0 werkzeug>=3.0.0 requests>=2.25.0 APScheduler>=3.10.0 -croniter>=1.3.0 \ No newline at end of file +croniter>=1.3.0 +bcrypt>=4.0.0 +cryptography>=41.0.0 \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 6828d7f..1219318 100644 --- a/templates/index.html +++ b/templates/index.html @@ -4,6 +4,8 @@