M1 完成:网络信号、Worker 求解器、源站 SDK。
M1 是"自适应"不再是
Mythic
README 里的形容词、而成为机制的里程碑。此前服务端只有一件给滥用
定价的工具 —— 工作量证明难度。M1 加入了让它能对谁在发问
定价的信号:JA4 TLS 指纹和 ASN 信誉输入一个带上限的风险引擎,
PoW 求解器搬离主线程,源站现在可以在 Node.js 和 Python 里原生
验证裁决。提交
bc87ed9 至 c7e4fc3。
本文在一次内部审计发现了原始"M1 完成"声明中的七处缺口后修订。
六处已在提交 067e9f7 中修复;一处仍诚实地敞开,下文会写明。
修正记录在此公开,而不是被埋起来。
为什么要信号 —— 以及为什么只做参考
反机器人的成败取决于信号,而每个孤立的信号都很弱。任何愿意费事的 客户端都能逐字节鹦鹉学舌一个 JA4 指纹。ASN 只是启发式 —— 今天是 数据中心,明天是住宅代理。把任何一个当作真理,你就是在用额外的 步骤建一个黑名单。
所以 M1 把每个网络信号都当作参考: 小而硬上限的贡献,可以提高一个请求的代价,但永远不能单方面放行 或定罪。流程是这样的:
图 1 —— 每个信号都只是参考且有上限。没有任何一个信号能单独放行或定罪一个请求。
JA4 指纹:有折扣、有上限、不被信任
ADR-0010。
服务端从 X-Mythic-JA4 读取 JA4 指纹,该头由终止代理
设置 —— 绝不直接来自客户端,因为客户端给出的任何值都受攻击者
控制。指纹与已知浏览器 JA4 模式匹配;命中后应用一个小的风险折扣,
上限让它无法单凭自己清掉一个挑战。格式错误的值和只有前缀匹配的
值会被完全排除在折扣路径之外(a8ce95a)。所有上限在
启动时校验;敌对的调参值拒绝服务(8f560bd)。
写明的限制:JA4 只是参考。逐字节鹦鹉学舌真实浏览器指纹的客户端 在这一层无法区分。折扣很小,正因为信号可伪造 —— 它只提高了朴素 脚本的成本,仅此而已。
ASN 评分:失败时关闭的启发式
同一个 ADR。客户端 IP 经 MaxMind GeoLite2-ASN 数据库
(MYTHIC_ASN_DB)解析为自治系统号。数据中心、托管和
VPN 出口 ASN 贡献风险;住宅 ISP ASN 不贡献。查找处处 nil 安全:
空的 MYTHIC_ASN_DB 会干净地禁用该信号,而已配置却
打不开的路径会在启动时致命。配置错误时关闭失败;缺席时按设计
静默降级 —— 因为那是运营者主动选择不启用。
审计修正:原始测试只覆盖了失败模式(nil、空路径、
坏路径、非法 IP)—— 从未真正跑过一次正向查找。
067e9f7 增加了 TestASNLookupPositive,
用仓库内生成的合成 .mmdb 夹具(非授权 MaxMind 数据)
钉住正向路径:数据中心网段得分,住宅网段不得分。
Web Worker 求解器:PoW 搬离主线程
SHA-256 暴力求解以前跑在主线程 —— 难度 24 以上页面会冻结数秒。
SDK 现在提供 solveInWorker(),把自包含的求解器
序列化成 Blob URL 在子线程跑。不需要单独部署 worker 文件;
不需要打包插件。进度回调每 2048 次迭代触发一次;
AbortSignal 终止 worker 并回收 URL:
export async function solveInWorker(
challenge: Challenge,
opts: SolveInWorkerOptions = {},
): Promise<Solution> {
// No Worker API (Node, SSR) → fall back to main thread.
if (typeof Worker === "undefined") {
const { solve } = await import("./pow");
return solve(challenge, opts);
}
const workerCode = `(${workerMain.toString()})()`;
const blob = new Blob([workerCode], { type: "application/javascript" });
const url = URL.createObjectURL(blob);
// …postMessage challenge, await solution, revoke URL
}
MythicClient 接受 useWorker(默认
true),构造时自动启动行为采集器。一个客户端,一条
流程,不需要手工接线。
白纸黑字写一个已知缺口:worker 路径至今只跑在 Bun 的
Worker 实现上 —— 从未在 Chromium、Firefox 或 Safari 里跑过。这记录
在 sdk/js/README.md 里,不做其他宣称。曾尝试搭建
Playwright 套件;构建环境下载不到浏览器二进制文件。在此改变之前,
真实浏览器行为未经测试、也不宣称。
源站验证:Node.js 与 Python
决策令牌以前只能在 Go(server/xtoken)里验证。源站现在
拿到原生助手。sdk/js/src/verify.ts ——
parsePublicKey(x) 把 JWKS 原始密钥包成 DER SPKI;
verifyToken(pub, token) 在原始载荷字节上校验 Ed25519
签名(不预哈希),强制规范 base64url 重编码,并拒绝过期令牌:
// Canonical encodings only: re-encoding the decoded bytes
// must reproduce the input exactly. Go's base64 decoder
// silently discards unused low bits of a trailing character,
// so distinct strings can decode to the same token.
if base64.RawURLEncoding.EncodeToString(payload) != payloadPart ||
base64.RawURLEncoding.EncodeToString(sig) != sigPart {
return nil, fmt.Errorf("%w: non-canonical encoding", ErrInvalidToken)
}
sdk/python/mythic/verify.py —— 经
cryptography 包实现相同语义:
parse_public_key、verify_token、
TokenClaims 数据类,任何拒绝都抛
TokenError。Python SDK 已进 CI
(.github/workflows/sdk-python-ci.yml),并跑在
make test 下 —— 此前没有任何东西自动执行它。
已跨语言验证并提交:e2e/crosslang.sh 从实时 Go 服务器
签发令牌,在 Go、Python、JS 三端逐一验证出一致结果
(dec=allow risk=6,三端相同)。
证据
| 套件 | 结果 |
|---|---|
go build ./... · go vet ./... | 干净 |
go test -race -count=1 | 7/7 个包,0 数据竞争 |
bun test (JS SDK) | 17/17 |
tsc --noEmit · bun run build | 干净 |
pytest (Python SDK) | 6/6 |
e2e/crosslang.sh | Go、Python、JS 在一个实时令牌上达成一致 |
评分契约本身被钉在代码里 —— 敌对调参无法把超范围的分值泄漏进 签名令牌(红队发现 H3,已回归测试):
// The score is a 0-100 contract (token claim `risk`,
// dashboards, tuning docs). Hostile tuning must clamp
// here, not leak 500 into tokens (red-team H3).
if score > 100 {
score = 100
}
if score < 0 {
score = 0
}
审计改变了什么
在原始"M1 完成"声明之后,一次从零开始的审计(全新克隆、每个套件
重跑、对验证路径做对抗性检查)发现了七处缺口。提交
067e9f7("fix(audit): close all 7 M1 audit gaps")
关闭了其中六处:
- ASN 正向测试已补(以前只有失败模式)。
- 行为提示现在在
verify()时发送 —— 此前采集器在挑战结束后就停了,导致服务端的 verify 阶段行为解析实际成为死代码。 - Python SDK 已加入 CI 和
make test。 - 跨语言 E2E 已提交(
e2e/crosslang.sh)。 docs/ROADMAP.md已修正 —— 它还把已完成的 M1 事项标为计划中。- 一个误导性的 worker 测试名已修正(它声称测的是回退路径,实际测的是真实 worker 路径)。
第七处 —— Web Worker 的真实浏览器测试 —— 仍敞开,作为已知缺口 记录,而不是宣称已完成。这就是 M1 当前的诚实状态。
开放问题
真实浏览器 worker 覆盖是上面唯一敞开的事项。除此之外:IP 信誉源 仍在计划中(ROADMAP,诚实地标着),M2 行为生物特征和 M5 跨节点 验证共享密钥库仍是下一个里程碑。按项目规则,每个新信号的绕过 方式都要在信号上线前先写进文档 —— 见 ADR-0010 的"Bypasses"节。
— XEY LABS