Require secure transport for Zhihu upstream - #517
Conversation
|
PR Title: Require secure transport for Zhihu upstream Commit: 本次变更对知乎 Open API 服务做安全加固:强制 HTTPS 并禁止关闭 TLS 证书校验。 主要改动:
整体评估:安全目标(任何路径都无法关闭 TLS 校验)已达成,因为 dispatcher 已被硬编码为 undefined,且 schema 会拒绝非 false 值。存在两点低危问题:(a) 运行时守卫用 |
|
PR Title: Require secure transport for Zhihu upstream Commit: 本次改动为知乎 Open API SDK 的 baseUrl 校验放宽传输层限制:在 https 之外,允许 http 仅用于回环地址 127.0.0.1 与 [::1](本地开发/联调场景),并同步更新 config.schema.json 的 pattern 与测试报错文案。整体方向合理:运行时守卫仍 fail-closed,非回环 http 一律拒绝;由于任意 https 原本就允许,该放宽不会新增攻击者对远程明文目标的利用面。 主要问题有两处:(1) config.schema.json 的 baseUrl pattern 采用 ^ 前缀匹配并要求回环主机后紧跟 : 或 /,与运行时 normalizeBaseUrl 的判定不一致——http://127.0.0.1(无端口/尾斜杠)会被 schema 拒绝但被运行时接受,而 http://127.0.0.1:8080@evil.com 这类 userinfo 变体会被 schema 放行但被运行时拒绝;(2) 安全边界放宽缺少对应的正向与边界测试,尤其 IPv6 分支依赖 URL hostname 序列化含方括号的实现细节,存在回归风险。 |
|
PR Title: Require secure transport for Zhihu upstream Commit: 本变更为 zhihu__open-api 服务收紧出站传输安全:1) config.schema.json 的 baseUrl pattern 由「https 或 loopback http」收紧为仅 ^https://;2) zhihu-open-api.js 移除 createTlsDispatcher/insecureTlsDispatcher 导入与实例,normalizeBaseUrl 拒绝所有非 https 协议,dispatcher 恒为 undefined;3) TLS 守卫由 firstDefined(...)===true 改为数组 .some(v=>v===true),修复了「前位显式 false 后位 legacy true 被绕过」的已确认缺陷;4) 测试更新错误文案并新增 legacy 别名被拒绝的断言;5) scripts/service-package-smoke.mjs 将 mock 上游从明文 http 改为自签 https,并为 daemon 进程设置 NODE_TLS_REJECT_UNAUTHORIZED=0 以放行测试连接。 评估:两条历史 finding(insecureTlsDispatcher 死代码、firstDefined 顺序绕过)均已被本次修订解决,未发现新的可利用安全问题或功能回归;schema 与运行时对「仅 https」的约束一致,smoke 脚本随之上线自签 TLS 以保持可运行。唯一发现是低严重度测试缺口:新增测试仅覆盖单标志为 true 的场景,而这些用例在旧的 firstDefined 缺陷实现下同样会通过,真正被 .some() 修复的「前位 false + 后位 true」组合缺少回归断言,建议补充以锁定修复行为。 |
|
PR Title: Require secure transport for Zhihu upstream Commit: 本次变更仅修改 scripts/service-package-smoke.mjs(+2/-1),是一次针对冒烟测试的 TLS 安全加固:将启动 octobus daemon 时的 NODE_TLS_REJECT_UNAUTHORIZED=0(全局禁用证书校验)替换为 NODE_EXTRA_CA_CERTS=mock.caFile(仅将本地 mock upstream 的自签名证书加入信任锚),并在 startMockUpstream() 返回对象中新增 caFile: certPath 字段。 核对结论:
|
| () => _test.resolveSettings({ config: { baseUrl: 'https://example', [alias]: true }, secret: { accessSecret: 'a' } }), | ||
| /TLS certificate verification cannot be disabled/, | ||
| ); | ||
| } |
There was a problem hiding this comment.
「前置 false + 后置 legacy true」组合缺少回归测试,新增用例在旧逻辑下同样通过
本次将 TLS 守卫从 firstDefined(...) === true 改为数组的 .some(v => v === true),正是为了修复历史确认的问题:当靠前标志显式为 false 而靠后的 legacy 别名为 true 时(例如 config: { skipTlsVerify: false, tlsInsecureSkipVerify: true }),firstDefined 会直接返回 false,守卫被绕过,本应抛出的「TLS certificate verification cannot be disabled」被静默跳过。但新增的测试仅覆盖「单个标志为 true」的场景(skipTlsVerify: true、tlsInsecureSkipVerify: true、insecureSkipVerify: true 各自单独出现)。在旧的 firstDefined 实现下,这些场景中靠前的标志都是 undefined(未定义),firstDefined 会继续向后取到 true 并照常抛出,因此这些新用例在修复前后行为完全相同,无法区分 bug 与修复。真正需要保护的回归场景(前位 false、后位 true 的组合)没有任何断言覆盖,将来若有人改回 firstDefined 或等价逻辑,测试套件依然会全部通过。
Problem code:
Changed code at services/zhihu__open-api/test/zhihu-open-api.test.js:523-528
Recommendation:
补充一个覆盖「前位 false + 后位 legacy true」组合的断言,锁定本次 .some() 修复的行为,例如:assert.throws(() => _test.resolveSettings({ config: { baseUrl: 'https://example', skipTlsVerify: false, tlsInsecureSkipVerify: true }, secret: { accessSecret: 'a' } }), /TLS certificate verification cannot be disabled/); 同理可对 bindings 层级与 insecureSkipVerify 各补一组。
Suggested diff:
diff --git a/services/zhihu__open-api/test/zhihu-open-api.test.js b/services/zhihu__open-api/test/zhihu-open-api.test.js
--- a/services/zhihu__open-api/test/zhihu-open-api.test.js
+++ b/services/zhihu__open-api/test/zhihu-open-api.test.js
@@ -520,6 +520,14 @@ test('respects config timeouts, custom headers, and legacy aliases', async () =>
for (const alias of ['tlsInsecureSkipVerify', 'insecureSkipVerify']) {
assert.throws(
() => _test.resolveSettings({ config: { baseUrl: 'https://example', [alias]: true }, secret: { accessSecret: 'a' } }),
/TLS certificate verification cannot be disabled/,
);
}
+ // 回归用例:前位显式 false + 后位 legacy true 在旧的 firstDefined 实现下会被绕过。
+ assert.throws(
+ () => _test.resolveSettings({ config: { baseUrl: 'https://example', skipTlsVerify: false, tlsInsecureSkipVerify: true }, secret: { accessSecret: 'a' } }),
+ /TLS certificate verification cannot be disabled/,
+ );
问题
知乎 Open API 服务允许使用明文
http://上游地址,并支持通过配置关闭 TLS 证书校验。影响
如果低权限配置可以修改这些选项,服务会通过明文传输或不可信 TLS 连接发送
accessSecret和 OAuth 凭证,可能造成凭证窃取和中间人攻击。修复内容
baseUrl强制使用https://。http://127.0.0.1和http://[::1]例外;其他明文 HTTP 地址仍被拒绝。skipTlsVerify、tlsInsecureSkipVerify和insecureSkipVerify只能为false。验证
node --check src/zhihu-open-api.js通过。node --check test/zhihu-open-api.test.js通过。cd services && npm run validate通过。service-l2-gate冒烟测试使用 loopback HTTP mock,不再因 schema 拒绝而失败。