Skip to content

PLAN-FNOS-017 fnOS 插件路由 403 误拒修复 ​

字段内容
计划编号PLAN-FNOS-017
计划日期2026-10-18
对应需求FNOS-017 fnOS 插件路由 403 误拒修复
本轮功能FNOS-017-01 至 FNOS-017-04
计划状态阶段一完成;真机验收待执行

计划目标 ​

让 fnOS 插件的同源判定只依赖「网关实际转发的那一套头」,消除远程域名访问下的 403 误拒,同时保持跨站拒绝强度不变。

当前实现与目标设计 ​

领域当前实现目标实现迁移影响
请求权威来源requestOrigin 优先取 x-forwarded-host,其次 host只取 host(网关写入的权威);x-forwarded-* 不参与同源比较无:网关始终会写 host
协议来源x-forwarded-proto → socket 加密状态socket 加密状态 → 常量 http(网关到插件是 loopback 明文)无:第二跳恒为明文
跨站信号sec-fetch-site: cross-site 直接拒绝不变无
无 origin 兜底localPeer(req)不变无
测试覆盖仅 loopback 直通与路由封装增加转发头同源、伪造 Origin、cross-site、非回环无 origin 四类需新增用例

为什么不是「多信任一种来源」 ​

另一种看似更省事的写法是:只要 x-forwarded-host 与 Origin 一致就放行。这会让任何能设置该头的中间层获得放行能力——而该头在本仓库内零写入(网关不设),完全来自外部。等于把同源校验交给请求方自证,属于安全倒退。

正确方向是让两侧都读同一个权威:网关已经把 host 改写成 loopback,插件的 requestOrigin 也应只认它,二者自然相等。这也符合网关代码里写明的设计意图——「translate a normal Origin to the same authority so strict same-origin plugin endpoints remain valid」。

影响范围分析 ​

需求功能代码模块配置/数据测试文档目标环境
FNOS-017-01plugins/dsh-fnos-plugin/src/host/authorized-directories.ts 的 requestOrigin无转发头同源用例排错文档补 403 排查真实 fnOS NAS 远程
FNOS-017-02同上(保持拒绝分支)无三类拒绝用例—本地构测
FNOS-017-03—无新增 4 条用例—本地构测
FNOS-017-04同上无loopback 沿用既有用例—DSH Web 内网

不涉及:网关转发头行为、DSH 宿主路由框架、授权目录 ACL 与路径校验、其它插件。

分阶段任务 ​

阶段一:修复与本地验证 ​

任务 ID对应需求/验收修改内容前置条件验证方式
PLAN-FNOS-017-T01-01FNOS-017-01 / AC-01requestOrigin 只用 host 构造权威,协议取 socket 加密状态(第二跳恒明文);保留对 x-forwarded-* 的忽略并写明原因无仿真脚本:带 x-forwarded-host 时判定由 403 变通过
PLAN-FNOS-017-T01-02FNOS-017-02 / AC-01~AC-03三处拒绝分支保持不变并补注释,说明各自拦截的攻击面T01-01三类拒绝用例全绿
PLAN-FNOS-017-T01-03FNOS-017-03 / AC-01、AC-02新增用例:带 x-forwarded-* 的同源请求放行、外部 Origin 拒绝、cross-site 拒绝、非回环无 origin 拒绝T01-01用例在修复前失败、修复后通过(逐条确认)
PLAN-FNOS-017-T01-04FNOS-017-04 / AC-01核对内网直连路径未受影响(既有用例 + loopback 直通断言)T01-01插件全部用例绿

阶段二:目标环境验收 ​

任务 ID对应需求/验收修改内容前置条件验证方式
PLAN-FNOS-017-T02-01FNOS-017-01 / AC-01~AC-03真实 fnOS NAS 经 https://<设备>.fnos.net 远程访问,验证授权目录、/fn 引用与插件详情页发布新版本并安装到设备接口返回 200 + 界面走查;证据登记 docs/validation/
PLAN-FNOS-017-T02-02FNOS-017-04 / AC-01同设备内网直连对照组T02-01行为与修复前一致

数据、权限和错误处理 ​

  • 本次只改「如何判断请求可信」,不改变任何数据读写路径与权限语义。
  • 拒绝分支保留精确错误码 remote-web-origin-not-trusted,便于排错时区分「被判定为跨站」与「路由内部失败」。
  • 若修复后仍出现 403,说明是另外两种预期拒绝(cross-site 或非回环无 origin),可据错误码与请求头直接定位。

依赖、风险和决策 ​

已决定事项 ​

  1. 不信任 x-forwarded-*:本仓库零写入该头,它属外部输入;信任它等于让请求方自证同源。
  2. 不做「两者取其一即可」的宽松判定:那会同时引入两种放行路径,攻击面翻倍。
  3. 不动网关:网关改写 origin 的行为是刻意设计(注释已写明),缺陷在插件侧的取值优先级。
  4. 保留 sec-fetch-site 直接拒绝:它由浏览器设置、不可被脚本伪造,是比 Origin 更强的信号。

风险 ​

风险影响缓解
某些部署下网关不写 host判定退化为无权威,可能 403 或走 localPeer网关代码恒定设置 host;补一条「host 缺失时回退 localPeer」的用例
平台侧另有反代直接打到 8787(不经网关)该路径下 origin 未被改写,可能仍不一致属部署差异;本次按「经网关」的标准链路修复,其它链路另议
修复被误读为「放宽同源」后续维护者照此扩大信任需求与代码注释都写明「收敛来源」而非「新增信任」

回退方案 ​

单文件函数级回退,恢复 x-forwarded-host 优先后行为即回到缺陷态;无数据迁移、无配置变更。

测试、打包、发布和回滚 ​

  • 本地:插件 check(typecheck + 单测),四类新增用例 + 既有 230 条用例。
  • 发布:补丁版本发布 @tnnevol/dsh-fnos,同步 published-dsh-plugins.json 与文档安装命令。
  • 目标环境:真实 fnOS NAS 远程访问验收,登记 docs/validation/。
  • 回滚:见上节。

参考资料 ​

  • 插件判定实现:plugins/dsh-fnos-plugin/src/host/authorized-directories.ts
  • 网关头改写:packages/fnos-gateway/src/middleware/request-headers.ts
  • 既有用例:plugins/dsh-fnos-plugin/tests/host/authorized-directories-persist-route.spec.ts
  • 相关需求:FNOS-001、FNOS-008(授权目录与 /fn 引用能力来源)

完成状态 ​

阶段任务状态
一:修复与本地验证T01-01~T01-04已完成
二:目标环境验收T02-01~T02-02规划中

变更记录 ​

日期变更说明
2026-10-18初始登记建立 PLAN-FNOS-017,两阶段 6 个任务。已写明「收敛来源而非新增信任」的方向选择与三种替代方案的否决理由、平台侧其它链路的边界、以及「host 缺失」的兜底用例要求。

基于 VitePress 构建