ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Mornye-Extension Amazon Music插件逆向解析:跨目录链接解析与CSRF会话刷新原理

Mornye-Extension Amazon Music插件逆向解析:跨目录链接解析与CSRF会话刷新原理

【免费下载链接】Mornye-Extension

Extension Registry for Mornye Browse, submit, and download community-built extensions.

项目地址:https://gitcode.com/gh_mirrors/sp/Mornye-Extension
点击查看免费下载

本文逆向解析 Mornye-Extension 仓库中的 Amazon Music 插件(v2.3.11),重点拆解两个最不易被察觉的核心机制:跨目录链接解析(如何把 Spotify/Deezer 链接转换为 Amazon ASIN)与 CSRF 会话刷新原理(插件如何对 Amazon Music Web API 维持一个"永不掉线"的会话)。全文以源码为据,所有结论均可在仓库中逐行验证。

🧩 先认识插件:它在仓库里的位置

Mornye-Extension 是一个面向 SpotiFLAC Mobile 的扩展注册表(Extension Registry),社区提交的扩展以.sflx压缩包形式存放于 extensions/,并通过 registry.json 记录版本与 SHA-256 摘要。

与"只交压缩包"的社区扩展不同,Amazon 插件属于附带可审查源码的内置 Provider:

文件/目录作用
sources/amazon/index.js插件主体(约 4000 行),含会话管理、链接解析、下载逻辑
sources/amazon/manifest.json权限白名单、下载质量档位(FLAC/Opus/Dolby Digital Plus/Atmos)等元信息
tests/amazon_session.test.cjsCSRF 会话刷新的离线回归测试
tests/amazon_resolution.test.cjs跨目录解析与 URL 校验的离线回归测试
tests/amazon_region.test.cjs区域目录归一化与 404 ASIN 重映射测试
extensions/amzn.sflx由 scripts/build_packages.py 构建出的可安装包

获取源码并运行回归测试(需要 Node.js 22+,测试全程离线、不访问真实音乐服务):

git clone https://gitcode.com/gh_mirrors/sp/Mornye-Extension cd Mornye-Extension node --test tests/*.test.cjs

🔐 CSRF 会话刷新原理

为什么必须用 POST 拿 token

Amazon Music 的 Web API(skill.music.a2z.com)要求每个请求携带一组设备凭据与 CSRF 令牌。插件的会话初始化函数 initSession 向以下地址发起POST请求:

https://music.amazon.com/config.json?skipToken=false&clientApplication=skyfire

这里有个关键的逆向发现,源码注释写得很直白:

// POST issues CSRF credentials; GET returns configuration without a token.

也就是说GET 请求虽然返回 HTTP 200,但响应中不再包含 CSRF token;只有 POST 才能拿到csrf.token / ts / rnd三件套,连同deviceId、sessionId一起存入会话对象 _session。

x-amzn-csrf 请求头的构造

每次调用 API 前,buildHeaders 都会拼装一套约 25 个x-amzn-*请求头,其中两个是 JSON 字符串:

"interface": "CSRFInterface.v1_0.CSRFHeaderElement", "token": "...", "timestamp": "...", "rndNonce": "..."

分别对应x-amzn-csrf(防跨站伪造)与x-amzn-authentication(客户端令牌)。rndNonce在缺失时会本地随机生成,避免请求因格式校验被拒。

失败即刷新:一次性的 refresh 与重试

initSession有一个兜底分支:若config.json拉取失败,就本地随机生成 deviceId/sessionId(源码注释坦承 "may get 429")。真正让会话"自愈"的是 refreshSession:

  • 调用失败(如 403/429)时,把_session.initialized置为false并重新走一遍initSession;
  • 各 API 封装(如 callShowHome)在首次失败后会refreshSession()+ 短退避,然后只重试一次。

tests/amazon_session.test.cjs 用vm沙箱加载真实源码验证了这条链路:搜索请求只 POST 一次 config.json 就复用会话;当模拟服务端以 403 拒绝首个搜索时,插件会重新 POST 刷新,第二次请求携带了token-2,断言序列为['token-1', 'token-2']。

🔗 跨目录链接解析原理

当 SpotiFLAC 拿到一首歌的 Spotify ID、Deezer ID 或 ISRC 时,插件需要在 Amazon 目录里"找到同一首歌"。入口函数是 resolveAmazonURL,它按优先级组织了一条回退链:

顺序输入手段
1Deezer ID(或用 ISRC 经 Deezer API 换得)抓取 Songlink 页面提取 amazonMusic 链接
2Spotify ID(必须形如 22 位字母数字,否则直接忽略)同上
3ISRC抓取 Songstats 页面,递归扫描 JSON-LD 的sameAs字段
4全部落空交给调用方回退到精确的 Amazon 目录搜索

从 Songlink 页面里"抠"出链接

callSongLinkPage 抓取https://song.link/{编码后的源URL}后,并不是正则匹配 HTML,而是 extractSongLinkNextDataJSON 直接定位 Next.js 注入的<script id="__NEXT_DATA__">,再解析props.pageProps.pageData.sections[].links[],挑出platform === "amazonMusic"的条目。数据来自结构化 JSON,比解析可见文本稳定得多。

严格校验:拒绝"看起来像"的链接

拿到候选 URL 后必须通过 acceptResolvedAmazonTrackURL → extractResolvedTrackASIN 的双重把关:

  • 域名必须命中 12 个官方music.amazon.*区域的白名单正则(L165),测试中music.amazon.com.evil.test这类仿冒域名会被拒;
  • 只接受明确的 track 路径或携带trackAsin参数的专辑深链,/albums/{id}、/artists/{id}一律不接受——因为外部解析器有时返回一个"ID 恰好长得像 ASIN"的艺人页;
  • ASIN 必须匹配^B[0-9A-Z]{9}$(ASIN_REGEX)。

源码注释还记录了一个真实坑:Apple Music 等 Provider 可能把外部数字 ID 塞进spotify_id字段,若不先用isLikelySpotifyId过滤,会拼出无效 Spotify 链接并"污染"整条 Spotify 链路(L3281-L3292)。

区域目录归一化与 404 时的 ASIN 重映射

Amazon Music 在不同国家站点(.com、.co.jp、.de……)使用互不相同的 ASIN。createAmazonContext 的策略是:用户贴来任何区域链接时,把请求上下文统一归一到标准目录music.amazon.com,但原区域 ID 先保留,等 Amazon 返回对应的标准 ID 后再"换票"。

下载时的兜底逻辑在 download 的 requestMedia:

  1. 先按原 ASIN 请求下载;
  2. 若收到目录级 404(TRACK_NOT_FOUND等),调用 getCanonicalCatalogTrack 做一次标准目录直查;
  3. 只有当返回的元数据携带一个不同的、经过校验的新 ASIN 时,才用它重试一次;
  4. 新 ASIN 仍 404、或鉴权失败(401/429)等情况,直接保留原始错误,绝不进入重试循环。

tests/amazon_region.test.cjs 对上述行为做了矩阵式覆盖:12 个区域域名全部归一、trackAsin参数在归一化中不丢失、"无新 ID 不重试"、"新 ID 仍 404 不循环"、"会话类错误不得触发 ASIN 重映射"等十余条断言。

✅ 小结

  • CSRF 会话刷新:POST 换取 token → JSON 请求头随每请求下发 → 失败后refreshSession()重建会话并只重试一次,兜底随机会话保证"永不卡死";
  • 跨目录链接解析:Deezer → Spotify → ISRC/Songstats 三级回退,结构化提取__NEXT_DATA__,配合域名白名单 + track 级 ASIN 校验,杜绝仿冒链接与字段污染;
  • 区域目录:请求统一走标准目录,下载 404 时最多一次"直查 + 换 ID 重试",保证失败可解释、不无限循环。

整套机制的每个关键分支都有对应的离线回归测试守护(见 tests/),这也是该仓库"源码 + 测试 + 构建产物"三位一体提交方式(见 README.md)的价值所在。

【免费下载链接】Mornye-Extension

Extension Registry for Mornye Browse, submit, and download community-built extensions.

项目地址:https://gitcode.com/gh_mirrors/sp/Mornye-Extension
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进