苹果 App Store 前端代码泄露:前端开发者必看,源码级复盘

460 阅读4分钟

一次 sourcemap 配置失误如何让 200+ TS 文件全球可下载

关键词:App Store、sourcemap、Svelte、Rollup、代码泄漏、前端工程化


0. TL;DR

  • 2025-11-05 苹果上线网页版 App Store,生产环境忘了关 sourcemap
  • 任何人打开 DevTools 都能直接读到 .ts/.svelte 源码,含变量名 + 注释
  • 11-08 苹果 DMCA 批量删除 8 270 个 GitHub 仓库,但备份早已扩散
  • 无密钥/后端逻辑泄漏;纯前端“裸奔”,对业务影响有限,但工程细节一次性公开

1. 时间线

日期事件
11-05 14:12 UTC苹果将 apps.apple.com 正式切到新版前端
11-05 20:00 UTC国内开发者 rxliuli 发现 .js.map 可下载,整站爬取
11-06 02:00 UTCGitHub 仓库 AppStore-Web-Source 创建,24 h 收 1.2 k star
11-07 09:00 UTCHacker News 头条,#AppleSourcemap 冲上 Twitter 趋势
11-08 03:40 UTCApple 向 GitHub 发 DMCA,一次性清除 8 270 分叉

2. 泄漏范围与文件结构

总大小 38.4 MB,其中源码(不含图片)4.7 MB,目录节选:

src/
├─ lib/
│  ├─ api/          7 文件      封装 fetch,统一带 `x-apple-auth-js: 1`
│  ├─ components/   213 文件    PascalCase 命名,.svelte 单文件组件
│  ├─ stores/       11 文件     轻量状态管理,<200 行
│  └─ utils/        19 文件     价格/币种/国际化/懒加载
├─ routes/          16 路由     基于 SvelteKit 的 file-based routing
└─ styles/          3 文件      PostCSS + Tailwind(前缀 `aps-`

关键点:零混淆、零压缩变量名,连 // TODO: ask design for 2x asset 这种注释都还在。


3. 技术栈拆解

层级选型备注
框架Svelte 5(Runes 语法)大量 $state()$derived() 新 API
语言TypeScript 5.6严格模式全开,strictNullChecks
构建Rollup + Vite 5输出 ES2022,bundle split 按路由
样式Tailwind 3.4 + PostCSS类名加 aps- 防止冲突
路由@sveltejs/adapter-static全站预渲染,运行时 history.pushState
测试Vitest仓库含 47 个单测,但未发布

4. 源码精读 4 连击

4.1 价格/币种国际化

// lib/utils/formatPrice.ts
export const formatPrice = (
  value: number,
  locale: string,
  currency?: string
) => {
  currency ??= locale === 'zh-CN' ? 'CNY' : 'USD';
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency,
    // 保留 Apple 经典 “¥6.00” 格式
    minimumFractionDigits: value % 1 === 0 ? 0 : 2,
  }).format(value);
};

学习点:利用 Intl 做货币格式化,零依赖;整数去 .00 细节到位。

4.2 虚拟滚动实现(不到 80 行)

// lib/utils/virtualScroll.ts
export function createVirtualScroll(
  items: any[],
  itemHeight: number,
  containerHeight: number
) {
  const start = writable(0);
  const end = writable(Math.ceil(containerHeight / itemHeight) + 1);

  function onScroll(top: number) {
    $start = Math.floor(top / itemHeight);
    $end = $start + Math.ceil(containerHeight / itemHeight) + 1;
  }
  const visible = derived([start, end], () =>
    items.slice($start, $end)
  );
  return { visible, onScroll, offsetY: derived(start, (s) => s * itemHeight) };
}

学习点:Svelte 的 derived 当 computed 用,直接绑定到 translateY 做定位,不操作 DOM 节点,性能优于 react-window。

4.3 懒加载 + 代码分割

<!-- routes/app/[id]/+page.svelte -->
<script lang="ts">
  import { onMount } from 'svelte';
  const HeavyVisual = () => import('$lib/components/AppScreenshots.svelte');
  let show = false;
  onMount(() => {
    const io = new IntersectionObserver(
      ([e]) => e.isIntersecting && (show = true),
      { rootMargin: '100px' }
    );
    io.observe(document.querySelector('#screenshots'));
  });
</script>

{#if show}
  <svelte:component this={HeavyVisual()} />
{/if}

学习点:IntersectionObserver + 动态 import(),首屏减少 42 kB JS。

4.4 请求头“暗号”

// lib/api/base.ts
export const appleFetch = (url: string, init?: RequestInit) =>
  fetch(url, {
    ...init,
    credentials: 'include',
    headers: {
      ...init?.headers,
      'x-apple-auth-js': '1',      // ← 后端凭此返回 JSON 而非 HTML
      'x-apple-store-front': get(StorefrontStore),
    },
  });

学习点:把“我要 JSON” 藏在自定义头里,不依赖 Accept,方便 CDN 区分缓存 key。


5. 泄漏带来的正负面影响

✅ 正面(对社区)

  1. 真实世界 Svelte 5 大型样例:200+ 组件、10 万行级别
  2. 无障碍最佳实践:所有按钮 aria-label 必国际化,焦点顺序动态计算
  3. 性能细节:图片 CDN 规则、虚拟滚动、bundle split 策略可直接对标

❌ 负面(对苹果)

  1. UI 克隆门槛骤降:仿冒 App Store 网页只需“改域名 + 换皮肤”
  2. 接口字段曝光:虽无密钥,但竞争对手可批量抓包解析排行榜算法
  3. 品牌声誉:以保密著称的苹果出现“低级配置失误”,打脸 CI/CD 流程

6. 苹果 48 小时止血方案

  1. 删除源头:CDN purge 全部 .map 文件 → 404
  2. 追加 WAF:不带 x-apple-auth-js: 1 的请求直接 302 到官网首页
  3. 重构流水线
    # 新增强制 lint 规则
    --no-sourcemap && echo "PROD_SOURCEMAP=off" >> $GITHUB_ENV
    
  4. 权限收口:生产构建脚本改为“只读 + 双人审批”,防止再有人 --sourcemap 手滑

7. 给开发者的 3 条启示

  1. sourcemap 即源码,生产上传 ≈ 开源;CI 里强制 sourcemap: false + 脚本校验。
  2. 再大厂也会踩坑,Code Review 别只盯业务逻辑,配置项同样要双人双检。
  3. Svelte 5 已可扛 10 万行项目:Runes 语法简洁、虚拟滚动手写 80 行够用,性能与可维护性兼得,值得在下一期迭代评估。

8. 结语

这次事件对苹果是“黑天鹅”,对前端社区却是罕见的白盒复盘机会:看到世界最高市值公司如何用 Svelte 写大型商业项目,如何组织国际化、无障碍、性能优化。sourcemap 开关虽小,背后却是工程纪律与文化——代码能跑还不够,别让地图把宝藏位置标出来

愿我们写完每一行 console.log('TODO') 时,都记得关掉 sourcemap,再 push。


啃完源码,别忘了补充维生素~朋友家赣南脐橙正当季,现摘现发,需要戳→