XEY LABS — MYTHIC / 003 EN/ ID/ 中文/ 日本語/ 한국어 ← BUILD LOG PROJECT REPO ↗ GITHUB ↗

M1 Selesai: Sinyal Jaringan, Worker Solver, Origin SDK.

M1 adalah milestone di mana "adaptif" berhenti menjadi kata sifat di README Mythic dan menjadi mekanisme. Sampai kini server menetapkan harga penyalahgunaan dengan satu instrumen — difficulty proof-of-work. M1 menambahkan sinyal yang memungkinkannya menetapkan harga atas siapa yang bertanya: fingerprint TLS JA4 dan reputasi ASN masuk ke risk engine ber-capped, solver PoW pindah dari main thread, dan origin bisa memverifikasi vonis secara native di Node.js dan Python. Commit bc87ed9 hingga c7e4fc3.

Postingan ini direvisi setelah audit internal menemukan tujuh celah dalam klaim "M1 complete" yang asli. Enam sudah diperbaiki di commit 067e9f7; satu tetap jujur terbuka dan dinyatakan di bawah. Koreksinya didokumentasikan di sini, bukan dikubur.

Mengapa sinyal — dan mengapa hanya advisory

Mitigasi bot hidup atau mati oleh sinyal, dan setiap sinyal sendirian itu lemah. Fingerprint JA4 bisa ditiru byte-per-byte oleh klien mana pun yang mau repot. ASN adalah heuristik — datacenter hari ini, residential proxy besok. Perlakukan salah satunya sebagai kebenaran mutlak dan kamu telah membangun blocklist dengan langkah tambahan.

Maka M1 memperlakukan setiap sinyal jaringan sebagai advisory: kontribusi kecil ber-capped yang bisa menaikkan harga sebuah request tetapi tak pernah bisa meloloskannya atau menghukumnya sendirian. Alirnya seperti ini:

JA4 fingerprinting: diskon, ber-capped, tidak dipercaya

ADR-0010. Server membaca fingerprint JA4 dari X-Mythic-JA4, yang diset oleh terminating proxy — tidak pernah dari klien langsung, karena nilai dari klien adalah attacker-controlled. Fingerprint dicocokkan dengan pola JA4 browser yang dikenal; kecocokan memberi diskon risiko kecil, ber-capped sehingga tak bisa sendirian meloloskan challenge. Nilai malformed dan match hanya-prefix ditolak dari jalur diskon sepenuhnya (a8ce95a). Semua cap divalidasi saat startup; nilai tuning yang hostile membuat server menolak melayani (8f560bd).

Batasan yang dinyatakan: JA4 bersifat advisory. Klien yang meniru fingerprint browser asli byte-per-byte tak bisa dibedakan di lapisan ini. Diskonnya kecil justru karena sinyalnya bisa dipalsukan — ia menaikkan biaya scripting naif, tidak lebih.

ASN scoring: heuristik yang gagal tertutup

ADR yang sama. IP klien di-resolve ke Autonomous System Number via database MaxMind GeoLite2-ASN (MYTHIC_ASN_DB). ASN datacenter, hosting, dan VPN-exit berkontribusi pada risiko; ASN ISP residensial tidak. Lookup-nya nil-safe di seluruh jalur: MYTHIC_ASN_DB yang kosong menonaktifkan sinyal dengan bersih, sementara path yang dikonfigurasi tetapi gagal dibuka bersifat fatal saat startup. Miskonfigurasi gagal tertutup; ketiadaan mengalami degradasi diam-diam sesuai desain — operator memilih untuk tidak mengaktifkannya.

Koreksi audit: test asli hanya mencakup mode kegagalan (nil, path kosong, path buruk, IP invalid) — tidak ada positive lookup yang pernah dieksekusi. 067e9f7 menambahkan TestASNLookupPositive terhadap fixture .mmdb sintetis yang di-generate di dalam repo (bukan data MaxMind berlisensi), mem-pin happy path: range datacenter ter-skor, range residensial tidak.

Web Worker solver: PoW keluar dari main thread

Grind SHA-256 sebelumnya berjalan di main thread — pada difficulty 24+ halaman membeku selama beberapa detik. SDK kini menyertakan solveInWorker(), yang mengubah solver yang self-contained menjadi string untuk Blob URL dan menjalankannya di luar thread. Tidak ada file worker terpisah untuk di-deploy; tidak perlu plugin bundler. Progress callback menyala setiap 2048 iterasi; AbortSignal mematikan worker dan mencabut 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 menerima useWorker (default true) dan otomatis memulai behavioral collector saat konstruksi. Satu klien, satu alur, tanpa wiring manual.

Celah yang diketahui, dinyatakan apa adanya: jalur worker hanya pernah berjalan di implementasi Worker Bun — tidak pernah di Chromium, Firefox, atau Safari. Ini didokumentasikan di sdk/js/README.md, tidak diklaim sebaliknya. Suite Playwright sempat dicoba; environment build tidak bisa mengambil binary browser. Sampai itu berubah, perilaku browser asli belum teruji dan tidak diklaim.

Verifikasi origin: Node.js dan Python

Token keputusan hanya bisa diverifikasi di Go (server/xtoken). Origin kini mendapat helper native. sdk/js/src/verify.ts — parsePublicKey(x) membungkus kunci mentah JWKS ke DER SPKI; verifyToken(pub, token) memeriksa tanda tangan Ed25519 atas byte payload mentah (tanpa pre-hash), menegakkan re-encoding base64url kanonis, dan menolak token kedaluwarsa:

// 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 — semantik yang sama lewat paket cryptography: parse_public_key, verify_token, dataclass TokenClaims, TokenError pada setiap penolakan. Python SDK kini di CI (.github/workflows/sdk-python-ci.yml) dan berjalan di bawah make test — sebelumnya tidak ada yang mengeksekusinya otomatis.

Terverifikasi lintas bahasa dan di-commit: e2e/crosslang.sh membuat token dari server Go yang hidup dan memverifikasinya secara identik di Go, Python, dan JS (dec=allow risk=6 di ketiganya).

Bukti

Paket ujiHasil
go build ./... · go vet ./...bersih
go test -race -count=17/7 paket, 0 data race
bun test (JS SDK)17/17
tsc --noEmit · bun run buildbersih
pytest (Python SDK)6/6
e2e/crosslang.shGo, Python, JS sepakat pada token live

Contract scoring-nya sendiri di-pin di kode — tuning hostile tidak bisa membocorkan skor di luar rentang ke dalam token bertanda tangan (temuan red-team H3, di-regression-test):

// 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
}

Apa yang diubah audit

Setelah klaim "M1 complete" yang asli, audit dari nol (fresh clone, setiap suite di-run ulang, adversarial pass atas jalur verify) menemukan tujuh celah. Commit 067e9f7 ("fix(audit): close all 7 M1 audit gaps") menutup enam di antaranya:

  • Test positif ASN ditambahkan (sebelumnya hanya mode kegagalan).
  • Hint behavioral kini dikirim di verify() — sebelumnya collector berhenti setelah challenge, membuat parsing behavioral tahap-verify di server sebagai dead code dalam praktik.
  • Python SDK ditambahkan ke CI dan make test.
  • E2E lintas bahasa di-commit (e2e/crosslang.sh).
  • docs/ROADMAP.md dikoreksi — ia masih menandai item M1 yang sudah selesai sebagai direncanakan.
  • Nama test worker yang menyesatkan diperbaiki (ia mengklaim mengetes fallback padahal mengetes jalur worker asli).

Yang ketujuh — test browser asli untuk Web Worker — tetap terbuka dan didokumentasikan sebagai celah yang diketahui, bukan diklaim selesai. Itulah keadaan M1 yang jujur saat ini.

Pertanyaan terbuka

Cakupan worker browser asli adalah satu item terbuka di atas. Selain itu: feed reputasi IP masih direncanakan (ROADMAP, ditandai jujur), dan M2 behavioral biometrics plus M5 shared keystore untuk verifikasi lintas node tetap menjadi milestone berikutnya. Sesuai aturan proyek, bypass setiap sinyal baru didokumentasikan sebelum sinyalnya dikirim — lihat ADR-0010 § "Bypasses".

— XEY LABS