TL;DR
Apple 服务端正在灰度启用新的认证门槛:登录请求必须携带由 Apple 私有签名组件生成的 X-Apple-ActionSignature,缺少该签名的第三方请求在凭证校验之前就会被拒绝(空 403),修改 UA、IP、端点均无效。ipatool 已在 v2.4.0(PR #525)修复:在用户态用 Unicorn 模拟器执行 Apple 官方的 CommerceKit/CoreFP 二进制来计算签名。Asspp 依赖的 ApplePackage 尚无对应实现。
背景与原理
Asspp 的登录依赖 ApplePackage 库,流程为:
GET https://init.itunes.apple.com/bag.xml?guid=<MAC>,从响应中取出authenticateAccount端点;- 向该端点 POST 一个 plist,字段为
appleId、password、attempt、guid、rmp=0、why=signIn; - 成功后返回包含
passwordToken和dsPersonId的 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-plist 和 application/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.access、com.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 组件。具体做法:
bag.xml中本就带有 SAP 配置字段:sign-sap-setup、sign-sap-setup-cert、sign-sap-version(当前版本 200);- 从 Apple 官方 CDN(swcdn.apple.com)用 HTTP Range 请求下载 OS X 10.9 更新包(
OSXUpd10.9.pkg),解 XAR/cpio 后按固定的 SHA-256 提取四个 Mach-O 二进制:CommerceKit、CommerceCore、CoreFP、CoreFPICXS; - 用 Unicorn CPU 模拟器在用户态加载并执行这些 Apple 私有库(自实现了一套平台 shim),在模拟环境中完成 SAP 握手:拉取 setup 证书、与
sign-sap-setup端点交换 buffer、初始化签名会话; - 登录请求体经该会话签名后,以 base64 放入
X-Apple-ActionSignature请求头发送,UA 等其他部分不变; - 登录固定使用 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 决定。
另外这次排查有一个值得记录的方法:用假账号重放请求。假账号和真账号得到完全相同的响应,可以直接证明拒绝发生在凭证校验之前,把”是不是我的账号出了问题”这个最容易浪费时间的可能性第一时间排除掉。这个方法对任何”所有请求都被拒”的问题都适用。
$ comment ./posts/2026/08-28-0a744a47-App-Store-第三方登录-403-问题排查记录.md