# 付费正文构建期加密与密钥鉴权下发

# Why

付费正文当前零防护。实测对 needPay: true 的算法付费页做无 Cookie、无 JS 的裸 curl:HTTP 200、60561 字节,提取出 4793 字符正文,与源 Markdown 全文 4958 字符基本等长,全文可读。

三条根因各自独立成立:

  1. VuePress 1 是构建期预渲染而非纯客户端渲染,每页 dist HTML 内含完整正文。
  2. Markdown 编译进 assets/js/*.js,无鉴权可直接下载。
  3. theme/components/Page.vuecontentHtmlShow = contentHtml.slice(0, 25000) 是从已渲染的完整 DOM 切预览,要能切必须先有全文。现有锁是一层帘子,不是一道门。

theme/util/api.js 现有 40 余个接口全属 user / message / weapp-question,没有任何正文下发接口,正文 100% 来自构建产物。

同时 .vuepress/generated-static-docs/ 下 140 个明文 JSON 既未被 git 跟踪也未被 .gitignore 覆盖,一次 git add -A 即会把全部付费正文明文提交入库。

# What Changes

  • 生成期用 AES-256-GCM 加密付费正文 HTML,构建产物只含密文、明文预览段与目录 headers。
  • 每篇独立密钥,由主密钥经 HMAC-SHA256(masterSecret, documentId) 派生,主密钥不进仓库、不进构建产物。
  • 新增 GET /api/interview/doc/key 密钥下发接口,服务端校验登录态与会员状态后返回该篇密钥。
  • 客户端用 WebCrypto 解密并注入正文,StaticInterviewDocument 从同步渲染改为异步解密渲染。
  • 明文预览段改为在生成脚本按节点边界切分,不再由客户端从全文切;预览段进构建产物承担 SEO。
  • Page.vue 的正文就绪检测与骨架屏状态机适配异步解密,覆盖取不到密钥、非会员、WebCrypto 不可用三条降级路径。
  • .gitignore 补充明文产物目录;密文 JSON 可入库,明文源 Markdown 不入库。
  • 保留 v 版本字段与 PAID_DOC_ENCRYPT 开关,支持一键回退到明文形态。

# Scope

本方案是提高门槛,不是访问控制。密钥必然在浏览器内存中出现一次,能打断点的用户仍可取得正文;已付费会员亦可循环调用接口取得全部密钥。目标是挡住 curl、view-source、禁用 JS、批量爬虫与顺手复制,不追求密码学意义上的不可破。真正的访问控制需要正文本身由服务端鉴权下发,不在本次范围。

已知代价:付费页可被搜索引擎索引的正文从全文缩减为预览段,长尾关键词覆盖会下降。这是任何有效防护的必然成本,已接受。

涉及两个仓库:

  • interview-vuepress-site(本仓):生成脚本、StaticInterviewDocumentPage.vueapi.js.gitignore
  • interview-koa2-backend:新增 routes/interviewClient/doc.js、密钥派生工具、conf/secretKeys.js、按用户维度限流

鉴权逻辑本身不需要新写——后端 middlewares/loginChecks.jscheckInterviewLogin 已提供 ctx.isViproutes/interviewClient/video.js 已有会员闸门先例,直接复用。

不修改支付流程、会员价格、登录鉴权逻辑、部署脚本与埋点。不引入新的运行时依赖,不新增第三方统计。

webapp
公众号
开发者导航
切换夜间模式
折叠侧边栏
收起全部