如何读懂 TranslateBooksWithLLMs 安全设计:路径遍历防护、密钥管理与上传文件安全指南
【免费下载链接】TranslateBooksWithLLMsTranslate full-length books and documents with Ollama, OpenAI-compatible, Gemini, Mistral, DeepSeek, Poe or OpenRouter. Preserves formatting. Resumes where you left off. No file size limits.项目地址: https://gitcode.com/gh_mirrors/tr/TranslateBooksWithLLMs
TranslateBooksWithLLMs 是一款使用 Ollama、OpenAI、Gemini、Mistral、DeepSeek、Poe 或 OpenRouter 翻译整本图书与文档的开源工具。本文带你深入它的安全设计:路径遍历防护如何阻挡恶意文件路径、API 密钥如何安全轮换、上传文件经过哪些安检关卡——即使是新手也能看懂这套"多层防御"。
🔍 安全设计总览:三道防线
该项目把安全能力拆成了清晰的三层,每层各司其职:
| 防线 | 核心模块 | 防御目标 |
|---|---|---|
| 路径层 | path_validator.py | 阻止路径遍历、任意文件读取 |
| 密钥层 | api_keys.py、key_pool.py | 防止密钥泄露、限流失败 |
| 文件层 | security.py | 危险扩展名、ZIP Slip、Zip 炸弹 |
🛡️ 路径遍历防护:让恶意路径无路可走
攻击者常试图用../../etc/passwd这样的路径"逃"出目录读取敏感文件。项目通过 PathValidator 类来拦截:
- 解析后再比较:
is_within_directory方法先对路径执行resolve()(跟随符号链接、规范化..),再用Path.relative_to逐段比较——而不是用字符串startswith。注释里明确说明原因:'/uploads-evil'会被startswith误判为在'/uploads'内部。 - 先查包含关系,再查文件存在:
validate_upload_path中,越界路径的错误提示永远不会泄露"这个文件是否存在",避免信息侧信道。 - 文件名双重校验:
validate_filename同时拦截绝对路径(含 Windows 盘符如C:)、../与..\序列、超过 255 字符的超长文件名。
想亲手验证这些行为?项目提供了 tests/unit/test_zip_slip.py 等测试文件,展示了"如果这些校验缺失会发生什么"。
ZIP Slip:档案解包的经典陷阱
EPUB 和 DOCX 本质是 ZIP 包,其中的条目名由攻击者控制。security.py 中的is_safe_archive_member函数会拒绝:
- 以
/开头的 POSIX 绝对路径、UNC 路径; - Windows 盘符路径(如
C:前缀); - 任一路径段等于
..的条目; - 含控制字符(包括 NUL)的名称。
配套的safe_extract_zip采用"全部校验通过才写入一个字节"的策略,被拒的压缩包不会留下任何残留文件——这就是纵深防御:名称检查之后,还会对解析后的实际落盘位置做第二道relative_to复核。
🔑 密钥管理:前端从不传输真实密钥
这是该项目最精巧的设计之一,见 api_keys.py:
- 哨兵值机制:当密钥配置在服务器
.env文件中时,前端界面发送的只是一个占位符__USE_ENV__,由服务端在 resolve_api_key 函数中还原为真实环境变量值。浏览器里永远看不到你的真实密钥。 - 单一可信来源:注释中提到,这段逻辑曾复制在四个路由文件中,分化导致 issue #200(占位符被原样转发给 Gemini)。现在所有端点共用一个解析函数,杜绝漂移。
- 端点隔离:
allow_env_fallback参数保证——当用户自行选择了目标服务器地址时,服务器存储的密钥绝不被发往客户端挑选的端点。
多密钥轮换:429 限流自动故障转移
如果你在.env中配置了多个同厂商密钥(逗号分隔),KeyPool 类会用轮询方式轮换它们:
- 某密钥收到 HTTP 429(限流)时,被标记为节流状态,下一个请求自动切换到其他可用密钥,无需等待;
- 使用单调时钟(
time.monotonic())记录节流截止时间,避免系统时间漂移导致的 bug; - 所有变更操作由
asyncio.Lock保护,可安全地在并发协程间共享。
完整的配置说明见 docs/API_KEY_ROTATION.md。
📁 上传文件安全:六步安检流程
每个上传文件都要经过 SecureFileHandler 的多重检查,流程设计值得细品:
第 1 步:文件名清洗用os.path.basename剥离一切路径成分,再正则拦截<>:"|?*及控制字符。随后用secrets.token_hex(8)生成随机前缀重命名文件——即使两个同名文件上传也永不冲突。
第 2 步:扩展名黑名单.exe、.bat、.js、.ps1、.iso等可执行与系统文件扩展名一票否决,即使允许"任意扩展名+内容探测"的宽松模式也绝不放行。
第 3 步:内容扫描
- 文本文件前 8KB 扫描
<script、javascript:、<?php、eval(等 16 种可疑模式,命中即拒; - EPUB/DOCX 内部条目再跑一遍
find_unsafe_archive_member,并拒绝包内.exe/.jar等可疑文件、条目数超过 10000 的Zip 炸弹; - PDF 校验
%PDF-头、页数上限 5000、拒绝加密文档,并探测扫描版(无文字层)给出友好警告。
第 4 步:临时文件原子落盘文件先写入.tmp临时后缀,内容校验通过后才重命名为正式文件;任何一步失败都通过_cleanup_temp_file清理,不会残留半成品。
第 5 步:大小与生命周期单文件上限 100MB;cleanup_old_files定期清理超过 24 小时的上传文件,防止磁盘被占满。
🚪 访问控制:会话令牌挡住"路过式"攻击
auth.py 解决了本地 Web 服务的经典问题:用户浏览网页时,恶意页面可能借浏览器向本地 API 发起跨域请求(drive-by attack)。
- 启动即铸造:每次服务启动用
secrets.token_urlsafe(32)生成约 256 位熵的随机令牌,随页面 HTML 交付给同源前端,从不持久化——进程重启即失效; - 全量门禁:
before_request钩子拦截所有/api/路由,令牌通过X-API-Token请求头或查询参数携带; - 防时序攻击:令牌比对使用
secrets.compare_digest恒定时间比较; - 最小豁免:仅页面本身、静态资源和健康检查端点
/api/health免令牌,且后者刻意不暴露任何敏感信息; - 配合 CORS 收紧:通配
Access-Control-Allow-Origin已移除,双管齐下同时封死跨域读取与 CSRF 向量。
相关回归测试见 tests/unit/test_api_auth.py。
✅ 给新手的三点启示
- 校验要"解析后比较":任何涉及路径的代码,都应在
resolve()之后用结构化方法(relative_to)而非字符串前缀判断包含关系。 - 密钥不该过网络:用
__USE_ENV__这类哨兵值让浏览器只传占位符,是把"配置"与"传输"解耦的经典手法。 - 安全测试要覆盖失败路径:从 tests/unit/test_zip_slip.py、tests/unit/test_api_auth.py 可以看出,该项目为每道防线都保留了独立测试,这正是安全能力不随重构腐化的保障。
更多供应商密钥与端点配置细节,可参考 docs/PROVIDERS.md;部署时的安全注意事项见 deployment/VALIDATION.md。
【免费下载链接】TranslateBooksWithLLMsTranslate full-length books and documents with Ollama, OpenAI-compatible, Gemini, Mistral, DeepSeek, Poe or OpenRouter. Preserves formatting. Resumes where you left off. No file size limits.项目地址: https://gitcode.com/gh_mirrors/tr/TranslateBooksWithLLMs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考