forked from MetaCubeX/ClashMetaForAndroid
-
Notifications
You must be signed in to change notification settings - Fork 4
Operator API Security zh
Nemu-x edited this page Jul 7, 2026
·
1 revision
Operator API 是一个受信任边界约束的扩展面。客户端将每个值都视为来自网络的不可信输入,并在其触及 UI、主题或磁盘之前施加严格校验。
本文描述客户端保证什么、不保证什么,以及我们明确防御哪些攻击。
相关攻击者类别:
- 恶意运营方 —— 控制订阅 URL 响应。可能发送恶意响应头(超大值、载荷、恶意 URL)。我们像对待任何外部输入一样对待它。
- 网络攻击者 —— 在受损网络上拦截订阅 HTTP 响应(罕见,因订阅走 HTTPS)。
- 运营方图床被攻陷 —— 徽标 CDN 被接管,开始提供恶意图片 / 重定向到内部服务。
- 应用商店审核者 / 监管者 —— 需要看到「品牌」无法悄然把应用变成另一个应用(身份改变仅限用户屏幕所见,而非应用行为)。
注:同时是订阅提供方的运营方,本就能在此 API 之外滥用许多东西(代理选择、路由规则)。品牌只是多了几个旋钮 —— 这些旋钮各自仍须安全。
- 去除首尾空白
- 去除控制字符(
U+0000–U+001F、U+007F,字段允许时保留\n/\r/\t) - 截断到字段最大长度(见响应头参考)
- 解码
base64:前缀 - 以上处理后,空白 → 字段视为不存在
仅当值以下列之一开头才接受:
-
https://、tg://、mailto:、t.me/(自动升级为https://t.me/)
品牌 URL 拒绝 http://。唯一例外是订阅 URL 本身的元数据探测,用户已明确选择明文订阅。
URL 还会通过 looksLikeUrl 检查(长度、无内嵌空白)。
- 仅正则
^#[0-9A-Fa-f]{6}$—— 不支持#RGB、rgba()、命名颜色。 - 客户端计算相对表面色的 WCAG 亮度。若对比度低于 3:1(AA「大文本」最低值),拒绝该颜色,使用默认主题强调色。
- 布尔:
true/1/yes/on→ true;false/0/no/off→ false;其他 → 响应头视为不存在。 - 枚举:仅接受文档中的值;未知值忽略。
-
Subscription-Userinfo流量:Long≥0;expire=须在[0, now + 10 年],否则忽略。X-Brand-Max-Devices:1–999。
这是风险最高的字段 —— 我们从运营方选定的 URL 拉取任意字节。防御:
- 仅 HTTPS。 http:// 在构建请求前即拒绝。
-
不重定向到私有网络。 每一跳前,解析出的 IP 会与以下比对:
-
10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、127.0.0.0/8、169.254.0.0/16 -
::1/128、fc00::/7、fe80::/10 - 以及其他 RFC1918 / RFC4193 / RFC6890 保留空间
-
- 连接 + 读取超时:各 15 秒 · 最大重定向:3
- 绑定设备自身 DNS 解析器(拉取期间无系统覆盖)
-
Content-Type须为image/png、image/webp或image/jpeg。其他(含image/svg+xml、image/gif、text/html)一律拒绝。 -
Content-Length(若存在)须 ≤ 512KB。若缺失,读取上限 512KB,超出即中止下载。 - 字节先以 bounds-only 模式由
BitmapFactory.decodeByteArray解析;仅当outWidth × outHeight × 4 ≤ 4MB(最大约 1024×1024)时才二次解码。 - 动图(animated WebP)仅解码首帧。
- 徽标缓存于
<filesDir>/brand/<sha256(url)>(应用私有) - 原子写入:
tmp+rename - 清除时机:所属订阅被删除;同订阅收到不同 URL 的新徽标
- 从不写入外部存储,其他应用不可访问
SVG 可携带类 XSS 载荷(<script>、指向远程资源的 xlink:href、CSS 导入、foreignObject HTML)。即便更安全的解析也无法绝对防止 DoS(billion-laughs、几何爆炸)。徽标是旗帜位图;PNG/WebP 足以覆盖用例而无 SVG 攻击面。
- 徽标是否真代表该运营方? 否 —— 我们不验证品牌所有权。
- 运营方网站是否真是其所有? 同上。
- 品牌名是否与面板一致? 否 —— 自由文本。
这些属运营方信任问题,而非客户端安全问题。用户添加此订阅时就已选择信任它。
按设计,品牌绝不以用户无法察觉的方式悄然改变应用身份:
- Android 启动器图标与包名从不改变。商店审核者始终能识别该应用。
- 关于页始终显示
<brand> — powered by ClashFest(不可翻译、固定)。 - 品牌激活时,设置中会有「Reset branding」,一键恢复默认身份。
品牌缓存(名称、徽标、强调色、链接)在以下情况被清除:
- 活动配置文件被移除(且无其他配置文件)
- 用户在设置中点击「Reset branding」
- 同订阅的后续拉取返回的响应头不再携带品牌值(清除品牌是运营方侧操作,而非永久卡住)
我们不在应用升级时重置品牌 —— 它会跨版本升级存留。
- 徽标拉取每个订阅更新周期最多一次
- 失败的拉取记忆 1 小时,避免重试风暴
-
SharedPreferences中的「活动品牌」缓存是更新之间的权威来源:断网不会在会话中途剥离品牌
若你在 Operator API 或其解析器中发现安全问题,请遵循仓库根目录顶部的安全策略。
📱 User Guide
- Getting Started
- Profiles & Nodes
- Routing & Rules
- Settings
- Deep Links
- Encrypted Subscriptions
- Troubleshooting
🏢 Operator API
📺 Companion