Files
bookmarks2csv/SECURITY_AUDIT.md

12 KiB
Raw Blame History

安全审核报告

审核日期: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。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、WHATWG template 文档模型、MDN CSP。
  • 验证:恶意 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 建议:

fix(security): harden bookmark parsing and CSV export

未执行 Git 提交;当前目录也不是 Git 仓库。