电子书权限不能只藏按钮:从书架列表到文件下载的服务端授权
本文目录
一本书的权限不是阅读页上的一个判断,而是覆盖元数据、封面、原始文件、转换文件、下载链接、后台操作、缓存和搜索索引的一组服务端策略。只保护详情接口,却让上传目录公开,仍然等于公开文件。
建立资源与操作矩阵
至少拆出 PUBLIC、LOGIN_REQUIRED、ADMIN_ONLY 等访问级别,再列角色和操作:匿名/登录读者/编辑/管理员分别能否发现、读取详情、打开文件、上传、审核、下架、删除和重试转换。列表可以只返回授权展示所需的标题、作者、封面与级别,不应夹带原文件路径、内部存储键、审核备注或可复用的下载地址。管理员隐藏书不能因为存在于数据库就进入普通用户搜索结果。
权限在每个 HTTP 请求上重新判定,依据服务端解析出的主体、当前账号状态、资源级别和对象归属。不要相信浏览器 localStorage 里的 role、客户端传来的 userId、书籍 id 难猜或按钮是否隐藏。书籍被下架、账号被禁用或角色撤回后,旧 token 和旧链接都要按明确的撤销策略处理。
文件交付的选择
最简单可靠的方式是受保护的文件代理:API 校验权限后读取私有存储并流式返回。大文件可采用短时签名 URL 减少应用带宽,但要把 URL 当作临时 bearer credential:谁拿到链接谁就可能在期限内访问。签名要限定对象、方法、过期时间和响应类型;撤销通常只能等链接过期,紧急场景则应走代理或支持撤销的存储层。
原始 AZW3/MOBI、转换后的 EPUB、封面、缩略图和备份是不同对象,不可只给一条转换产物加权限。存储应位于 web root 外,文件名由服务端生成,数据库只保存受控对象键。不要把文件系统路径直接拼进请求或错误信息。
转换队列也是权限边界
上传时先鉴权、限制大小、核对扩展名与实际内容、隔离保存,再排入转换任务。任务 payload 使用内部 bookId,不接受调用者任意输入输出路径。worker 读取前复核书籍状态,转换目录不可执行,限制 CPU、内存、页数、输出大小和时长;zip/HTML/XML 解析器也要保持更新。转换失败时保留安全错误码,不把临时路径或完整异常返回给读者。
只有管理员上传或审核通过的用户上传才可进入公开/登录可见书架。用户上传默认 PENDING;审核拒绝、下架、恢复和删除都应留下操作者、时间和原因类别的审计记录。审核不能只检查标题,需有版权投诉和举报处理入口。
缓存和可观测性
受限正文不能进入匿名共享 CDN 缓存。对详情 API 采用 private/no-store,或者使用严格包含权限范围的缓存键并在角色变更时失效。日志记录 bookId、结果码、耗时和 requestId,不记录签名 URL、Authorization、书籍正文或邮箱。速率限制与下载并发控制用于降低滥用,但无法阻止已获授权用户复制自己可以读取的内容;不要宣传成“绝对防爬”。
验收用例
| 主体 | 书架元数据 | 详情 | 阅读文件 | 管理审核 |
|---|---|---|---|---|
| 匿名 | 仅公开及允许展示的登录书元数据 | 公开书可读 | 按书籍级别判定 | 拒绝 |
| 普通用户 | 按可发现规则返回 | 登录书需有效会话 | 每次检查状态和权限 | 拒绝 |
| 编辑 | 仅被授权范围 | 依职责和对象范围 | 仅可读审核通过内容 | 仅授权审核操作 |
| 管理员 | 可管理 | 依后台策略 | 依后台策略 | 允许且有审计 |
再测试过期/撤销 token、猜测其他 bookId、直接访问原文件路径、下架后旧 URL、并发下载、转换重试和缓存命中。预期拒绝必须是正确 HTTP 状态,不能返回 200 加一段 Forbidden 文本。
参考:OWASP Authorization Cheat Sheet、OWASP File Upload Cheat Sheet。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。