TL;DR

Apple 服务端正在灰度启用新的认证门槛:登录请求必须携带由 Apple 私有签名组件生成的 X-Apple-ActionSignature,缺少该签名的第三方请求在凭证校验之前就会被拒绝(空 403),修改 UA、IP、端点均无效。ipatool 已在 v2.4.0(PR #525)修复:在用户态用 Unicorn 模拟器执行 Apple 官方的 CommerceKit/CoreFP 二进制来计算签名。Asspp 依赖的 ApplePackage 尚无对应实现。

背景与原理

Asspp 的登录依赖 ApplePackage 库,流程为:

  1. GET https://init.itunes.apple.com/bag.xml?guid=<MAC>,从响应中取出 authenticateAccount 端点;
  2. 向该端点 POST 一个 plist,字段为 appleIdpasswordattemptguidrmp=0why=signIn
  3. 成功后返回包含 passwordTokendsPersonId 的 plist,后续搜索、下载都使用这个 token。

最近第 2 步开始稳定返回 403,响应体为空:

HTTP/2 403
server: Apple
x-responding-instance: MZFinance:3340259:::
apple-timing-app: 11 ms
content-length: 0

在 Asspp 上即表现为输入任意账户密码组合稳定报错“认证失败:服务器返回 HTTP 403 且响应体为空”。

x-responding-instance: MZFinance 说明请求到达了 Apple 应用层,是应用层返回的 403,而非边缘节点拦截。

本文记录排查过程。

排查过程

端点

bag.xml 返回的是 legacy 端点 buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/authenticate,403。

改用新端点 auth.itunes.apple.com/auth/v1/native/fast/,返回 204,响应体同样为空。

两个端点都不可用。

出口 IP

临时在 Cloudflare 上部署了一个转发 Worker,从美国 SJC 节点发送相同请求,legacy 端点仍返回 403,native 端点返回无 Location 的 302。同时 bag.xml 从本机访问正常(200),排除整段 IP 被封。排除 IP 因素。

TLS 指纹

curl 使用 LibreSSL,ApplePackage 使用 BoringSSL(swift-nio-ssl)。改用 macOS 原生 URLSession(Apple 自有 TLS 栈)重试:legacy 返回无 Location 的 301,native 返回 403 HTML 错误页。结果不变。

排除 TLS 指纹因素。

另外注意到,拒绝方式不固定:同一个请求在多次尝试中会分别得到 403、204、301、302,共同点是响应体都不含 plist。

User-Agent 与 Content-Type

测试了 Configurator 2.17/2.18/2.19、iTunes UA,以及 application/x-apple-plistapplication/x-www-form-urlencoded 两种 Content-Type,结果均为 403。

排除。

账号状态

使用一个不存在的 Apple ID 和错误密码发送相同请求,响应与真实账号完全一致(空 403)。

说明拒绝发生在凭证校验之前,与账号和密码是否正确无关。

原因

ipatool 的 issue #522 中有人贴出了对 AppleMediaServices / CommerceKit 的逆向分析,要点如下:

  • store 凭证(passwordToken)现在由 AMSMediaTokenService 以 Private Access Token(Privacy Pass)形式签发;
  • 令牌使用 Secure Enclave 中的 P-384 密钥签名,密钥经 SecKeyCreateAttestation 完成硬件 attestation;
  • 密钥的使用受 appstoreagent 进程的 Apple 签名 entitlement(com.apple.security.attestation.accesscom.apple.keystore.absinthe 等)和 SEP 硬件 ACL 限制,由 AMFI 校验代码签名。

即:登录现在要求请求携带有效的 SAP 签名(X-Apple-ActionSignature),而现代 macOS 上该签名的生成链条绑死了 Apple 硬件和 Apple 签名的系统进程。当时据此判断第三方程序无法构造——这个结论后来被推翻了,见下文「修复」。

同一 issue 中的其他信息佐证了这一结论:

  • GrandSlam SRP 登录(Apple ID 账号层)仍然可用,2FA 短信可正常收到,失败只发生在商业接口签发 store 凭证这一步;
  • Apple 自己的 iTunes 12.6.5.3 同样无法登录;
  • iMazing 也受影响,官方称将发布修复;
  • 该变更按 storefront / pod / 账号灰度推送,持有未过期 passwordToken 的账号暂时不受影响,重新登录时才会失败。

修复

ipatool 在 2026-08-28 发布了 v2.4.0,核心变更是 PR #525:移除旧的认证实现,改为 SAP 签名请求。后续的 v2.5.0(2026-08-31)又补了 SAP 超时处理和瞬时认证响应重试。

实现方式没有走 Secure Enclave,而是走了 Apple 自己留下的旧路——OS X 10.9 时代的软件 FairPlay 组件。具体做法:

  1. bag.xml 中本就带有 SAP 配置字段:sign-sap-setupsign-sap-setup-certsign-sap-version(当前版本 200);
  2. 从 Apple 官方 CDN(swcdn.apple.com)用 HTTP Range 请求下载 OS X 10.9 更新包(OSXUpd10.9.pkg),解 XAR/cpio 后按固定的 SHA-256 提取四个 Mach-O 二进制:CommerceKitCommerceCoreCoreFPCoreFPICXS
  3. 用 Unicorn CPU 模拟器在用户态加载并执行这些 Apple 私有库(自实现了一套平台 shim),在模拟环境中完成 SAP 握手:拉取 setup 证书、与 sign-sap-setup 端点交换 buffer、初始化签名会话;
  4. 登录请求体经该会话签名后,以 base64 放入 X-Apple-ActionSignature 请求头发送,UA 等其他部分不变;
  5. 登录固定使用 bag 中的 SAP 配置,不再允许回退到旧的无签名流程。

关键点是:签名不是逆向重写的,而是直接运行 Apple 自己编译的代码算出来的,服务端无法区分。Unicorn 是纯解释执行,不依赖 JIT,理论上 iOS 上也能跑。

需要注意的是,这条路线同样随时可能被 Apple 封堵:它成立的前提是服务端继续信任 10.9 时代的 CoreFP 组件。Apple 可以在服务端停止接受旧版本签名(比如提高 sign-sap-version 的最低版本、强制校验硬件 attestation,或让旧组件无法完成 setup 握手),届时同样的空 403 会再次出现。ipatool 把 sign-sap-version 硬编码为 200,也意味着服务端一旦升级版本,客户端就必须跟着适配。

对 Asspp 的影响:ApplePackage(Swift)需要等价的实现才能恢复登录。直接移植的成本不低——需要一个 Mach-O 加载器、一套 Unicorn 绑定和足够完整的平台 shim;更现实的路径是等 ApplePackage 上游跟进,或者把 ipatool 的 SAP 模块作为独立进程/库接入。

结论

403 是 Apple 服务端有意为之的变更,不是 Asspp 或 ApplePackage 的缺陷,修改请求参数无法解决,但签名本身可以用 Apple 的旧组件在用户态算出。ipatool 已经验证了这条路径可行。

写在最后

个人对这类变更是没有还手之力的。Asspp 的本质是一个调用 Apple 私有接口的第三方客户端,接口规则由 Apple 单方面决定,随时可以收紧掐死,这次只是又一次而已。

不过这次的过程也说明了另一面:issue #522 里基于逆向得出的”理论上无法构造”,四天后就被 ipatool 用模拟执行 Apple 旧组件的方式绕开了。服务端门槛再高,只要 Apple 自己的旧代码还被服务端信任,就有路可走。当然这条路能用多久,同样由 Apple 决定。

另外这次排查有一个值得记录的方法:用假账号重放请求。假账号和真账号得到完全相同的响应,可以直接证明拒绝发生在凭证校验之前,把”是不是我的账号出了问题”这个最容易浪费时间的可能性第一时间排除掉。这个方法对任何”所有请求都被拒”的问题都适用。