审计范围:认证、跨用户隔离、文件系统路径、上传、WebDAV、OPDS、SQL。
resolve_namespace:secure 模式下所有业务接口必须携带accessToken=username:token,严格比对users.token(find_user+ 等值校验),不匹配即login_required。- 命名空间不可由参数覆盖:namespace 恒来自 token 解析出的用户名——传其他用户名的 token 无法访问他人书架/数据。
- token 存储于 users 表(单会话;
reset_user_password会清空 token 强制下线)。 - WebDAV:Basic 认证 →
gen_encrypted_password校验 +enable_webdav门控;home 严格限定storage/data/{user}/webdav。
files.rs:home参数白名单:__HOME__/__WEBDAV__/__LOCAL_STORE__/__STORAGE__(后者需 manager)/ 空——其余一律"非法访问"。resolve_secure_path:组件级归一化(..逐级弹出,越出 base 即拒绝)+starts_with(base)最终校验——所有 list/save/mkdir/delete/upload/download 均走此函数。- upload 文件名只取
basename(../x、a/b收敛为安全名)+ 跳过隐藏文件。
resolve_storage_path(本地书解析):canonicalize+starts_with(storage_dir)校验。opds.rs下载/获取同样经白名单路径解析。
- 全部使用 sqlx 参数化绑定(
?1/?2/...),无字符串拼接 SQL。
- 文件名/路径均收敛(见上);书源导入仅 JSON 白名单字段;本地书导入只做解析不入壳执行(EPUB zip 解包 + TXT 编码检测)。
book_source_cookies表:user_namespace + source_url联合主键——书源登录态(cookie/user_agent)严格按用户命名空间存取,cookie_for(ns, url)只读本命名空间行,跨用户不可见、不可覆盖。- 抓取入口(
crawler::fetch_book)按当前请求命名空间注入 cookie;FlareSolverr 返回的 cookie 与用户原 cookie 按 name 合并后仍存回该用户命名空间。
- 仅当环境变量
FLARESOLVERR_URL配置时才启用(默认禁用,零外部依赖)。 - 仅书源抓取(
fetch_book)命中 Cloudflare 质询特征(503 + 特征 HTML)时转发;RSS/TTS 等原始抓取(fetch/fetch_get)不经 FlareSolverr。 - 转发请求携带当前用户的 cookie(保持书源会话连续性),响应 cookie 按 name 合并后按用户存库,UA 一并记录。
- 原实现:
token = md5(username + now_millis)——时间戳可猜测,存在 token 伪造风险。 - 现实现:
uuid::Uuid::new_v4()随机 token(32 位十六进制,不可预测)。 - 旧 token 因存于 DB 不受影响;下次登录即换新随机 token。
- 失败 5 次 → 锁定 5 分钟(用户名 + IP 双键计数,
x-forwarded-for/x-real-ip优先;无代理头时退化为按用户名计数);成功登录清零。 - 覆盖登录/注册入口:锁定中直接拒绝(「尝试过多请稍后」),不泄露用户是否存在。
- 纯内存计数(进程内有效)——单实例部署无需配置;多实例部署需前置外部限流(见已知限制)。
- 每次登录生成新
uuid v4token 并追加到users.token_map(每用户上限 5 个并存会话,超出淘汰最旧);任一 token 均可通过resolve_namespace认证。 - 设备间互不干扰:登出/改密只清除当前设备 token(
reset_user_password清空全部 token 强制全线登出)。 - 配合 token 过期(GAP 118):
users.last_login_at+READER_TOKEN_TTL_DAYS(默认 30 天)——过期 token 拒绝访问,需重新登录;legacy 迁移数据(last_login_at=0)同样按过期处理。
- multipart 上传统一上限:
READER_UPLOAD_MAX_MB(默认 100MB)——覆盖书籍/文件上传、备份恢复、图片等所有 multipart 入口;超限返回 413 + 明确错误提示(含环境变量名),不再依赖代理层限流兜底。 - 与既有防护叠加:文件名 basename 收敛 + 路径白名单 + 本地书解析只读不入壳。
- 注意:正文/封面等非 multipart 接口不受此限制(数据量小);超大文件仍建议代理层
client_max_body_size与上传上限匹配(见 README 部署节)。
- 密码哈希为 MD5 双层(
md5(md5(pw+salt)+salt))——legacy 算法兼容,旧密码不重设无法换算法。缓解:HTTPS + 强密码策略;未来可加 bcrypt 并存迁移(登录取新哈希升级)。 - accessToken 走 URL query——可能进入代理/访问日志。缓解:部署 HTTPS(服务端已支持 TLS 配置可查 README)。
登录无速率限制已解决:登录限流(用户名+IP 失败 5 次锁 5 分钟)已内置(见上);多实例部署时内存计数不跨进程,仍需前置反向代理限流。multipart 上传无大小上限已解决:READER_UPLOAD_MAX_MB(默认 100MB)统一限流(见上)。- EPUB zip 解压无条目大小/数量限制——zip 炸弹防护缺失;已由上传上限(同 4)缓解,公网建议保持代理层限制。
- 非 secure 模式:恒走
default命名空间。 - secure 模式:Basic(独立 OPDS 账号优先 → 系统用户账号)或
accessToken=username:token(与 /reader3 同套校验)。
- 存储于
system_settings键值表(opds_account),格式{salt}${sha256_hex(salt || password)}(util::sha256:16 字节随机盐,非明文)。 - 与系统用户(legacy 双 md5 兼容哈希)分离:配置后仅用于 OPDS Basic 认证,不产生系统用户、不占用户配额。
- 认证顺序:独立账号(sha256 校验)→ 系统用户(
gen_encrypted_password双 md5 校验)→ accessToken。
- 复用白名单路径解析,不越出用户存储目录。