「反正没人拿到 URL」不能作为不做访问控制的理由

今天给一个单人 PRD 填表工具加云端草稿箱,产品思维盲区暴露:「我这个 URL 别人猜不到,所以不用做身份验证」——完全站不住脚。「概率低」只能降低防护成本,不能取消防护。顺便记零知识加密的落地方案。

场景

我在给一个单人内部使用的 PRD 填表工具加云端草稿箱功能。产品诉求很朴素:能换设备接着填。

工具本身部署在 xxx.pages.dev/some-tool 这样的 Cloudflare Pages 上,配了 noindex 挡搜索引擎,没做任何登录 / 授权。

产品负责人的原话:

「需要增加一个线上的草稿箱,身份验证的问题需要考虑吗?没拿到链接谁也进不去。

听起来天经地义。但这句话是个幻觉,而且是很多人做小工具时都会踩的幻觉


为什么「没拿到链接谁也进不去」不成立

拆开 4 层:

1. noindex 只挡搜索引擎,不挡任何有链接的人

CF Pages / Vercel / Netlify 这类静态托管,默认对任何发起 HTTP 请求的人都返回内容。<meta name="robots" content="noindex"> 只让 Google / Bing 不索引,但任何拿到 URL 的人都能直接访问

2. URL 泄漏路径远多于想象

盘一下真实世界里 URL 会「意外」跑到哪:

  • 发到 Telegram / 微信 / Slack:服务端明文存
  • 手机截图里带着地址栏:iCloud / Google Photo 自动同步
  • 浏览器书签同步:多设备协同 = 多设备风险面
  • 未来任何一次协作时贴给别人:@某人时就漏了
  • CF Pages access log:CF 内部员工理论上可见
  • 复制粘贴时误发:发错群

这些路径不需要你「故意」,你只要这个工具,泄漏概率就是不为零的

3. URL 本身可以猜

  • 域名 xxx.pages.dev 是公开可查的 CF 子域
  • 路径 /some-tool 是自然英文,不是随机哈希

即便概率很低,「不可能」和「概率低」是两个概念。攻击者只要能自动化扫,时间就是成本

4. 一旦加了云端存储,风险等级质变

这是最关键的一层:

  • 只有本地存储(localStorage)时:URL 泄漏 → 别人只看到工具本身,你的数据在你自己浏览器里
  • 加了云端存储、没身份验证时:URL 泄漏 → 别人能看你的实际内容,能覆盖你的实际内容

这是两个数量级的安全等级差


「概率低」能推出什么

P(泄漏) 极小 只能推出:

  • ✅ 不用上企业级零信任
  • ✅ 不用请安全审计
  • ✅ 防护方案可以低成本

不能推出:

  • ❌ 不用做访问控制
  • ❌ 不做加密

这是我今天想沉淀的一条原则:

「概率低」是「防护成本可以低一档」的理由,不是「不做防护」的理由。

反过来想更清楚:如果「概率低」可以推出「不做」,那我们所有的备份、异地容灾、断路器都可以撤了 —— 因为「机器不会那么容易挂」也是一个「概率低」的判断。

工程决策不看单次概率,看「失败时的代价 × 单位时间累积概率」,后者才是决定要不要做防护的东西


我给出的落地方案(自留)

回到那个 PRD 填表工具,给的方案是密码 + Cloudflare KV + 前端 AES-GCM 加密的零知识(zero-knowledge)架构:

┌─────────────────────────────────┐
│  Tool (浏览器)                  │
│  用户输密码 → 前端 AES 加密     │
│  (密码永不发到服务器)           │
└──────────────┬──────────────────┘
               │ HTTPS
               ▼ 加密后的 blob
┌─────────────────────────────────┐
│  Cloudflare Worker              │
│  只转发,不解密                 │
└──────────────┬──────────────────┘


┌─────────────────────────────────┐
│  Cloudflare KV                  │
│  存的就是密文 base64            │
└─────────────────────────────────┘

核心原则:

  1. 密码只在浏览器里,派生成 AES key(PBKDF2-SHA256 210k iterations),从不发到服务器
  2. 服务端全程只看到加密后的乱码,KV 里存的都是密文
  3. owner ID 用 HMAC-SHA256 派生,服务端看到的是 16 位 hex 索引,不知道对应的是谁
  4. Worker 加 rate limit 挡暴力猜密码(每 IP 每分钟 60 次)
  5. body size guard(比如 100KB)防打爆 KV

攻击面诚实告知:

  • 攻击者拿到 URL → 能看工具、能拉密文 → 猜密码,PBKDF2 210k 让暴力破解慢到不现实
  • 攻击者拿到 URL + 密码 → 能全读全改(这就是密码泄漏的后果,符合预期)
  • 密码忘了 → 草稿彻底丢(零知识的代价,不可逆)

成本对比:

方案工程量别人拿到 URL 后果
A · 裸奔(不做)30 分钟能读能改所有草稿
B · 密码 + KV + 加密1.5 小时密码错就 403,读改都不行
C · GitHub OAuth 登录3-4 小时只有指定账号能进

单人自用场景选 B,不选 C —— C 是过度设计,给一个人做多用户身份系统是浪费。未来真要多人协作,B 再升 C 也不贵


归纳

小工具不做安全,通常不是想不到,而是脑子里出现了「反正没人会盯上我这么小的东西」的幻觉。把这个幻觉拆掉,决策路径就清楚了:

  1. 只要功能会存用户实际数据 → 访问控制是硬需求,不管使用规模多小
  2. 「概率低」不能作为「不做」的理由,只能作为「防护成本可以低一档」的理由
  3. 零知识架构在「服务端也不信」的场景里特别值 —— 前端加密的一点点工作量,换来服务端 zero-liability,长期收益远超短期投入

留一句给未来的自己:

每次想说「反正 xxx 概率很低」的时候,把这句话补完:「所以我把防护成本降到 xxx」,而不是「所以我不做」。


马启航Marvis