Files
bookmarks2csv/SECURITY_AUDIT.md

130 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 安全审核报告
审核日期:2026-10-14。范围:`F:\Codes\HTML\convert` 当前目录全部文件,包括隐藏文件。审核开始时目录仅有 `index.html`(68 行);全文人工审查并进行了危险 API、网络调用、凭据模式扫描。后续也审查了新增的回归测试。
## 1. 项目概况与依赖
项目是本地离线书签 HTML → CSV 转换器,使用 HTML、CSS、原生浏览器 JavaScript;无框架、后端、数据库、浏览器扩展、桌面 IPC、移动端代码或 CI/CD 配置。
| 清单项 | 结果 |
| --- | --- |
| 直接第三方依赖及实际版本 | 0;没有 npm/CDN/其他第三方库 |
| 锁文件及关键间接依赖实际版本 | 无锁文件,0 个可识别的间接依赖 |
| 包管理器、依赖清单 | 无;未发现 package.json、requirements.txt、go.mod、Cargo.toml、pom.xml、Podfile 或同类文件 |
| 构建、现成测试 | 无;直接由浏览器打开 HTML |
| 停止维护、长期未更新、疑似仿冒包 | 无适用第三方包 |
因此没有需要逐包检索的名称或版本,也没有可运行的项目级 npm audit / pip-audit / cargo audit 等依赖审计。不为此安装依赖或制造清单。已通过网络查询 OWASP、MDN、WHATWG,核对代码风险与防护方式。没有声称项目受某个 CVE 影响。
浏览器属于外部运行环境;其安装版本、扩展和系统漏洞不属于此项目依赖图,仍需保持浏览器更新。
## 2. 问题汇总
下表“位置”为修复后代码行;各项详情同时给出审核前行号,方便对应原问题。
| 编号 | 类别 | 严重级别 | 位置 | 状态 |
| --- | --- | --- | --- | --- |
| S01 | 代码 | High | index.html:26、48 | 已修复;数据变化已获开发者同意 |
| S02 | 代码 | Medium | index.html:31、48 | 已修复 |
| S03 | 代码 | Medium | index.html:5、34 | 已修复;原行为是否触发请求取决于浏览器 |
| S04 | 代码 | Low | index.html:41、47 | 已修复 |
| S05 | 代码 | Low | index.html:62、87 | 建议关注 |
| S06 | 代码 | Low | index.html:70、79 | 已修复 |
未发现 Critical 问题;未发现第三方依赖漏洞或真实硬编码凭据。静态审核和回归测试不能证明不存在所有漏洞。
## 3. 已修复问题与利用条件
### S01:CSV 公式注入(High,条件性风险)
- 原位置:`index.html:44–47`。标题仅转义双引号,未处理公式起始字符。
- 条件:攻击者影响网站标题或提供恶意书签文件;用户转换后用会识别 CSV 公式的表格软件打开。是否发生外部请求、敏感数据读取或进一步危害取决于软件、公式功能及安全提示;不声称所有环境都能执行系统命令。
- 修复:统一 `toCsvField()` 编码;对公式符号、相关全角符号、前置空白后的公式符号及开头控制字符添加可见 `文本:` 前缀。所有字段仍使用双引号包裹并转义内部双引号。
- 取舍:开发者明确选择了“给危险标题加文本前缀”。正常标题保持原样;危险标题的底层 CSV 数据会变化。采用普通文字前缀避免依赖某个软件对单引号或制表符的特殊解释。
- 参考:[OWASP CSV Injection](https://community.owasp.org/attacks/CSV_Injection)。OWASP 明确指出没有适用于所有表格软件和下游消费者的通用策略。
- 验证:覆盖 `= + - @`、全角变体、前置空白、Tab、CR、LF、NUL 及实际转换输出。Excel / LibreOffice 的打开、重新保存、再次打开行为**待确认**,本次没有启动这些应用。
### S02:URL 字段未做完整 CSV 编码(Medium)
- 原位置:`index.html:45–47`。URL 直接拼接入带引号的字段;未统一转义内部引号。
- 条件:构造浏览器 URL 序列化后仍包含特殊分隔内容的 href,并由下游 CSV 解析器处理。例如 `httpx:payload","=1+1` 会被原来的 http 前缀检查接受,而且浏览器 href 保留双引号;这与 S04 的宽松协议检查相关。普通 HTTP(S) URL 中的引号通常会被编码,不能把每个双引号 URL 都当成可利用案例。
- 风险:CSV 结构损坏;在可突破字段边界的输入/解析器组合下可能插入额外字段或公式。
- 修复:标题和经标准 URL 解析的 URL 都调用同一个 CSV 编码函数,采用 CRLF 行分隔,保留中文 BOM。
- 验证:双引号、逗号、换行、HTML 实体、含引号 URL;浏览器确认上述自定义协议能保留引号,新实现拒绝该输入。浏览器将普通 HTTP(S) 测试 URL 中的引号编码为 `%22`,输出保持两个字段。
### S03:导入 HTML 的外部资源请求风险(Medium)
- 原位置:`index.html:36–37`。使用 DOMParser 解析不可信 HTML,没有资源加载策略。
- 条件:用户选择包含 img、iframe 等资源标签的恶意 HTML;使用会在此解析过程中加载资源的浏览器。原始项目在特定浏览器是否发出请求**待确认**,本次未对修复前版本做动态复现。
- 风险:向攻击者或本机/内网地址发起客户端请求,泄露 IP、访问时间等信息;不属于后端 SSRF,也没有证据表明原代码能读取跨源响应。
- 修复:在未挂载的 template 内容中解析,只读取链接文本和 href,不将导入节点移入活动 DOM;增加 `default-src 'none'` 的 CSP,脚本和样式仅允许匹配 SHA-256 哈希的本地内联内容,禁用 base 和表单提交。删除内联 onclick,改为事件监听器,将内联状态样式移入 style 块。
- 参考:[MDN DOMParser.parseFromString](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser/parseFromString)、[WHATWG template 文档模型](https://html.spec.whatwg.org/dev/syntax.html)、[MDN CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy)。
- 验证:恶意 img / iframe / stylesheet 资源均指向回归测试的本机计数端点;收到 0 次请求。导入 script 与事件处理器未执行,导入节点未进入页面。
### S04:协议校验使用字符串前缀(Low)
- 原位置:`index.html:46`。`startsWith('http')` 也允许 `httpx:` 等自定义协议。
- 条件:恶意文件包含以 http 开头的自定义协议;下游软件打开链接,且系统安装了对应协议处理器。
- 风险:非预期协议进入导出结果。转换器自身不会导航到这些链接,没有证据表明这构成直接代码执行。
- 修复:使用无 base 参数的 `new URL()` 解析原始 href,只允许 `http:`、`https:`。不受导入 HTML 的 base 标签影响。非法 URL、相对 URL、file/data/javascript/自定义协议均跳过。
- 验证:大小写 HTTP 接受;其他协议与相对 URL 拒绝。
### S06:未释放下载对象 URL(Low)
- 原位置:`index.html:54–60`。创建 Blob URL 后没有释放。
- 条件:同一页面反复转换,尤其处理较大的书签文件。
- 风险:累积保留 Blob 内存,可能造成页面资源压力。
- 修复:下载锚点在 finally 中移除,随后延迟释放对象 URL,保留下载触发时间。
- 验证:真实 FileReader 产生 Blob,下载文件名正确,URL 被释放。测试截获下载点击并检查 Blob 内容,没有验证操作系统实际保存文件的交互。
### 其他加固
- 状态输出使用 textContent。原来的 innerHTML 仅插入数字计数,没有证明原处存在 XSS;这是减少危险输出 API 的加固。
- 读取失败给出用户可理解的状态提示,不输出文件内容或路径。
- 将“100% 安全私密”的绝对声明改为准确的本地处理、不上传书签说明。没有在软件 UI 放入审核过程备注。
## 4. 密钥、敏感文件与其他检查
- 对全部当前文件进行了 API Key、AccessKey、Token、密码、私钥头、常见云/平台密钥模式、数据库连接串、Webhook 地址检索,并人工检查原始文件全部内容;未发现真实凭据。新增测试中的 `file:///secret` 是拒绝本地协议的测试数据,不是密钥或真实文件路径。
- 未发现 `.env`、证书、私钥、配置、示例、文档或构建产物中的真实秘密。没有需要迁移到环境变量的配置,因此不创建空的 `.env.example`。
- 当前目录不是 Git 仓库,没有 `.git` 或 `.gitignore`;无法确认历史版本、上级其他仓库、远程仓库或已发布副本曾否包含秘密。若以后加入环境文件,应忽略实际 `.env` 并只提交变量名示例。
- 若开发者知道任何凭据曾提交或泄露,应到对应平台作废并重新生成;删除代码不足以消除泄露。
- 未发现 SQL/NoSQL、命令执行、eval、动态代码加载、服务端模板、文件系统路径拼接、解压、反序列化、加密、TLS 关闭、敏感日志、跨窗口消息、凭据存储代码。
- CSRF、服务端 SSRF、CORS、认证/越权、浏览器扩展权限、桌面 IPC/WebView、CI Action 固定版本检查无适用实现。
- 允许用户选取 HTML 是功能输入;accept 属性仅是选择提示,不作为安全边界。导入内容不执行、不挂载、资源加载受策略限制。
- HTTP(S) 书签仍可指向钓鱼网站;转换器不替用户审核目标网站信誉,也不自动访问目标网站。
## 5. 开发者决定与剩余风险
没有尚未获得批准、必须阻断本次修复的事项。公式前缀的数据变化已获同意;没有依赖大版本升级、删除功能、密钥轮换或旧数据结构兼容修改。
**S05:超大文件造成页面卡顿(Low,建议关注)**
- 位置:`index.html:62、87`,完整读取文件后在主线程解析,并在内存中生成 CSV。
- 条件:用户主动选择极大或结构复杂的 HTML;风险主要是当前页面可用性。
- 方案 A:设定明确的文件大小上限;方案 B:保留无限制输入,未来按实际规模考虑分块处理;方案 C:增加用户确认或可取消处理。
- 建议:先依据真实书签文件大小确定上限。未擅自选取阈值,以免拒绝原本可转换的正常文件。这是后续产品取舍,本次保留原行为。
其他限制与建议:
1. 本次浏览器测试使用本机 Chrome,通过临时 HTTP 服务加载;直接 file:// 打开、Firefox、Safari 与实际 Excel/LibreOffice 打开 CSV 的结果**待确认**。没有扩大“已验证”的范围。
2. CSP 哈希必须随内联脚本/样式更新;浏览器计算时使用规范化后的换行。新增测试会检查哈希是否匹配,后续改动需同步计算 SHA-256 哈希。
3. 若未来通过服务器公开部署,应在 HTTP 响应增加合适的 CSP 和 `frame-ancestors` / 点击劫持防护;meta CSP 不支持所有响应头能力。目前没有服务器配置可修。
4. CSP 和 template 防护依赖浏览器正确实现;保持浏览器更新,避免安装会读取页面和文件内容的不可信扩展。
## 6. 验证与改动文件
- 修改:`index.html`,完成上述修复与加固。
- 新增:`security-tests.cjs`,仅使用 Node 内置模块和已安装 Chromium 浏览器,无新增第三方依赖。使用独立临时浏览器配置和本机服务器,不使用用户现有浏览器资料。
- 新增:本报告,作为项目审核交付物,不在软件 UI 中展示。
- 原项目没有构建或测试命令,因此不存在可运行的既有构建。执行 `node --check security-tests.cjs` 通过;执行 `node security-tests.cjs` 通过:**31 项浏览器检查、CSP 哈希一致、导入资源请求为 0**。
- 首次沙箱内启动 Chrome/Edge 异常退出;在获得执行环境自动审批后使用沙箱外 Chrome 完成测试。测试期间发现并修正 CSP 哈希换行规范化问题,最终测试通过。
Conventional Commits 建议:
```text
fix(security): harden bookmark parsing and CSV export
```
未执行 Git 提交;当前目录也不是 Git 仓库。