Source Map 泄露实战复盘:从 Anthropic 512K 行代码泄露看构建供应链安全——面向多受众的深度技术分析与防御体系建设指南

事件回顾:一次配置失误引发的代码裸奔

2026年3月31日凌晨,安全研究员 Chaofan Shou 在 X 平台披露:AI 领域头部企业 Anthropic 的明星产品 Claude Code 因构建配置失误,其完整 TypeScript 源代码(约 1,900 个源文件、512,000+ 行代码)被公开暴露。讽刺的是,泄露的代码中包含一套名为 "Undercover Mode" 的防泄密系统——专门设计用于防止 Anthropic 员工在使用 Claude Code 向公共开源仓库提交代码时,意外暴露内部代号、未发布版本号及 Slack 频道等信息。这套用于保护代码安全性的子系统源码,最终随着整个代码库一起被泄漏。

值得注意的是,"Undercover Mode" 等敏感功能在正式构建时会被 dead-code-elimination 优化移除,不会出现在生产运行的代码中。然而,source map 文件包含了构建前的完整原始源码,因此这些被消除的代码仍然被暴露。

泄露路径复盘:

Bun 构建(默认生成 external source map)
    ↓
未配置 .npmignore 排除 *.map 文件
    ↓
cli.js.map(约 60MB)随 npm 包发布到 registry
    ↓
攻击者下载 npm 包 → 直接解析 cli.js.map 文件
    ↓
提取 sourcesContent 字段中的完整 TypeScript 源码(1,900+ 文件,512K+ 行代码)

部分来源指出,map 文件还包含了对 Cloudflare R2 存储桶的引用,但主要泄露途径是 sourcesContent 字段直接内嵌源码。

历史背景:The Register 报道称 2025 年早些时候 Anthropic 曾修补过类似的 source map 暴露问题,此次为第二次发生。这暴露了构建流程中持续性的配置管理风险。

⚠️ 安全提示: 2026年3月31日当天,npm 生态还发生了另一起独立的 axios 供应链攻击。若您在 00:21-03:29 UTC 期间通过 npm 安装/更新了 Claude Code,请检查 lock 文件中是否存在 axios 版本 1.14.1 或 0.30.4,这些版本可能包含恶意代码。


漏洞成因

用"图纸与保险箱"比喻理解 Source Map

想象您建造了一座金库(生产环境应用):

  • 压缩后的代码 = 金库外观(公众可见,但看不出内部结构)
  • Source Map = 建筑图纸 + 保险箱密码 + 内部通道地图(本应锁在保险柜里)

正常情况: 工程师调试时需要图纸,但图纸绝不离开内网。

泄露情况: 施工队不小心把图纸钉在了金库大门上,任何人都能取走。

Source Map 的技术本质

Source Map 是构建工具生成的 JSON 格式调试映射文件,包含两个致命字段:

字段 内容 风险
sources 原始文件路径列表 暴露项目目录结构、技术栈、内部代号
sourcesContent 对应文件的完整源代码 直接还原原始 TypeScript 代码,无需反编译

关键认知误区: "我们只是发布了压缩代码,原始代码很安全"——错。Source Map 文件(如 cli.js.map)随 npm 包公开发布,任何人可通过 npm pack @anthropic-ai/claude-code 下载。解析其中的 sourcesContent 字段即可直接提取原始 TypeScript 源码,无需任何反编译操作。

Claude Code 的特殊风险点

此次泄露的严重性在于双重失误:

  1. 构建层: Bun 默认生成 external source map(约 60MB 的 cli.js.map)
  2. 发布层: 未过滤 .map 文件,且 map 中硬编码了指向 Cloudflare R2 存储桶的 URL

泄露的不仅是代码,更是攻击面。 通过 source map 泄露,攻击者能获得:

信息类型 业务风险
完整业务逻辑 竞争对手复制核心算法
未文档化 API 端点 绕过前端限制直接调用后端
硬编码密钥/凭证 直接接管系统权限
内部系统架构 精准定位后续攻击目标
开发者注释 暴露内部流程和敏感信息(如"Undercover Mode")

真实案例: 2021 年 GETTR 平台因 source map 泄露,暴露了未授权密码修改端点,导致攻击者利用该漏洞进行账户接管,最终造成 9 万用户数据被爬取。


检测方式

2.1 被动检测:已有资产的 source map 暴露排查

浏览器 DevTools 检测法(最直观)

  1. 打开目标网站,按 F12 进入 DevTools
  2. 切换到 Sources 面板
  3. 观察文件树:
    • 若看到 src/components/ 等原始目录结构 → 已暴露
    • 若只看到 main.abc123.js 等压缩文件 → 相对安全

直接访问探测法

# 自动化探测脚本
for url in $(cat urls.txt); do
  for map in "main.js.map" "app.js.map" "index.js.map" "bundle.js.map"; do
    status=$(curl -s -o /dev/null -w "%{http_code}" "${url}/${map}")
    if [ "$status" = "200" ]; then
      echo "[!] ALERT: ${url}/${map} exposed (status ${status})"
    fi
  done
done

代码仓搜索法

# GitHub 代码搜索
"sourceMappingURL" extension:js

# PublicWWW HTML 源码搜索引擎
sourceMappingURL

搜索引擎挖掘法

site:example.com filetype:map
site:example.com "sourceMappingURL"

2.2 主动检测:CI/CD 流水线中的 source map 泄露预防

方案一:包内容审计(npm 生态)

# 发布前 dry-run 检查
npm pack --dry-run 2>&1 | grep -E "\.map$|sourceMapping"

# 或解压检查
npm pack && tar -tzf *.tgz | grep -E "\.map$"

方案二:GitHub Actions 集成检测

# .github/workflows/security-scan.yml
name: Source Map Leak Detection

on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Check for source maps in build output
        run: |
          npm ci
          npm run build
          if find dist -name "*.map" | grep -q .; then
            echo "❌ ERROR: Source maps found in build output"
            echo "::error::Source maps detected in build artifacts"
            exit 1
          fi
          
      - name: Audit npm package content
        run: |
          npm pack --dry-run > pack.log
          if grep -E "\.map$" pack.log; then
            echo "❌ ERROR: Source maps will be published to npm"
            echo "::error::Source maps detected in npm package"
            exit 1
          fi

方案三:使用 TruffleHog 扫描 CI/CD 日志

# 扫描 CircleCI 日志中的 source map 泄露证据
trufflehog circleci --token

# 扫描 GitHub Actions 日志
trufflehog github --repo=<repo> --workflow

2.3 高级检测:供应链层面的 source map 监控

npm 包 source map 专项审计

# 安装包后检查 node_modules
find node_modules -name "*.map" -type f | head -20

# 检查特定包的 source map 内容(验证是否包含源代码)
cat node_modules/<package>/dist/index.js.map | jq '.sourcesContent | length'
# 若返回数字 > 0,说明包含内联源码

企业级 SCA(软件成分分析)工具集成

  • 在 SCA 扫描规则中增加 *.map 文件检测
  • 对第三方依赖进行 source map 暴露风险评估
  • 建立内部依赖缓存,过滤含 source map 的包

修复方案

3.1 构建层:彻底禁用生产环境 source map

决策树:选择适合你的方案

是否需要生产环境调试?
├── 是 → 使用 hidden-source-map + 错误监控平台托管
│   └── 需要即时调试?
│       ├── 是 → 私有 source map 服务器(IP 白名单限制)
│       └── 否 → 按需生成(报错时从 CI 系统关联)
└── 否 → 完全禁用(推荐)
    └── 需要错误监控?
        ├── 是 → 上传至 Sentry 后删除本地 map
        └── 否 → 不生成 map 文件

⚠️ 特别警示: 绝对禁止在生产环境使用 external 或 source-map 模式后直接通过 CDN/NPM 托管。本次事件的核心失误正是 external 模式生成了独立 map 文件,且未在发布阶段将其排除。

Webpack 配置

// webpack.config.js
module.exports = {
  // 生产环境:完全禁用 source map
  devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',
  
  optimization: {
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          format: { comments: false }, // 移除所有注释
        },
        extractComments: false, // 防止生成额外的注释文件
      }),
    ],
  },
};

Vite 配置

// vite.config.js
export default defineConfig({
  build: {
    sourcemap: process.env.NODE_ENV !== 'production',
  },
});

Bun 配置(Claude Code 事件相关)

# bunfig.toml - 关键:显式禁用
[build]
sourcemap = false

⚠️ 补充检查: 除配置文件外,需检查 CI 脚本中的 bun build 命令,确保未附加 --sourcemap 参数,防止命令行参数覆盖配置文件。建议优先使用命令行显式禁用:

# 构建命令行显式禁用
bun build --sourcemap=none ./src/index.ts --outdir ./dist

环境变量控制(通用方案)

# .env.production
GENERATE_SOURCEMAP=false

3.2 发布层:npm 包 source map 过滤

package.json 白名单机制

{
  "name": "your-package",
  "files": [
    "dist",
    "!dist/**/*.map",  // 显式排除 source map
    "README.md"
  ],
  "scripts": {
    "prepublishOnly": "npm run build && npm run clean:maps && npm run verify:no-maps"
  }
}

.npmignore 黑名单机制(备用方案)

# .npmignore
*.map
sourcemaps/

发布前强制检查脚本

{
  "scripts": {
    "clean:maps": "find dist -name '*.map' -delete",
    "verify:no-maps": "if find dist -name '*.map' | grep -q .; then echo '❌ Source maps detected! Aborting publish.'; exit 1; fi"
  }
}

3.3 运行时层:已泄露 source map 的访问控制

若因调试需要保留 source map,必须实施访问控制:

Nginx 配置(IP 白名单)

location ~* \.map$ {
    # 仅允许内部网络访问
    allow 10.0.0.0/8;
    allow 192.168.0.0/16;
    deny all;
    
    # 或基础认证
    auth_basic "Source Maps";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

Express 中间件(动态控制)

app.use('*.map', (req, res, next) => {
  const allowedIPs = process.env.ALLOWED_DEBUG_IPS?.split(',') || [];
  if (allowedIPs.includes(req.ip)) {
    next();
  } else {
    res.status(403).send('Forbidden');
  }
});

3.4 替代方案:安全的生产调试架构

方案 A:Source Map 上传至错误监控平台

// webpack.config.js - 生成但不部署
devtool: 'hidden-source-map', // 生成 map,但不添加 sourceMappingURL 注释

// CI/CD 中上传至 Sentry 后删除
sentry-cli releases files <release> upload-sourcemaps ./dist --validate
rm -rf ./dist/*.map

方案 B:按需生成 source map

  • 保留构建产物和 source map 在内部 CI 系统(如 GitLab Artifacts)
  • 生产报错时,通过内部系统关联 map 文件还原堆栈
  • 不将 map 文件暴露至公网

3.5 应急响应:已泄露 npm 包的处置

第一步:立即废弃版本

# 将泄露版本标记为废弃(任何时间都可操作)
npm deprecate @anthropic-ai/claude-code@2.1.88 "SECURITY: Source code leaked via source map. Upgrade immediately."

# 若泄露严重且在发布后 72 小时内,可尝试 unpublish
npm unpublish @anthropic-ai/claude-code@2.1.88

# 超过 72 小时:需联系 npm 支持 (security@npmjs.com) 并提供安全事件证明
# 超过 30 天:无法下架,只能通过 deprecate 标记和发布修复版本

第二步:强制版本升级

  • 发布修复版本(如 2.1.89),确保无 source map
  • 在 README 和 Release Note 中明确警示:"2.1.88 版本存在源码泄露,请勿使用"
  • 通过 npm audit 等渠道通知下游用户

第三步:凭证轮换

  • 立即轮换所有在泄露代码中出现的 API 密钥、数据库凭证、JWT 密钥
  • 检查 Git 历史中的注释是否包含内部系统信息

3.6 容器化场景的补充检查

对于使用 Docker 部署的应用,增加以下检查:

# Dockerfile 中的最佳实践
COPY --from=builder /app/dist ./dist
# 构建后立即删除 source map
RUN find ./dist -name "*.map" -delete

# 或使用多阶段构建,确保最终镜像不含 map

运行时验证:

# 检查 Docker 镜像中的 map 文件
docker run --rm your-image find /app/dist -name "*.map"

# 若输出不为空,说明镜像存在泄露风险

管理层面的优化措施

4.1 流程层:将 source map 管控纳入 SDL(安全开发生命周期)

强制检查点(Quality Gate)

阶段 控制措施 责任方 工具/脚本
代码提交 Pre-commit hook 检测 .map 文件 开发团队 git diff --cached --name-only \| grep "\.map$"
构建阶段 CI 自动扫描构建产物中的 source map DevOps find dist -name "*.map"
发布阶段 npm pack --dry-run 强制检查 安全团队 npm pack --dry-run \| grep ".map"
部署阶段 生产环境 WAF 规则拦截 .map 请求 运维团队 Nginx/Cloudflare 规则
运营阶段 定期外部扫描(红队/渗透测试) 安全团队 自定义扫描脚本

Pre-commit Hook 示例(优化版)

# .git/hooks/pre-commit
if git diff --cached --name-only | grep -q "\.map$"; then
  echo "❌ ERROR: Source map files should not be committed"
  echo "Run 'npm run clean:maps' to remove them"
  exit 1
fi

4.2 工具链层:构建安全的 CI/CD 流水线

关键原则:默认拒绝,显式允许

  1. 标准化构建镜像: 封装已禁用 source map 的构建工具配置,开发团队不可修改
  2. 供应链隔离: 使用私有 npm registry(如 Verdaccio、Nexus),自动过滤含 source map 的第三方包
  3. Artifact 审计: 部署前强制扫描 tarball/zip 内容,阻断含 map 文件的包
  4. SBOM 增强: 将 source map 暴露风险纳入软件物料清单(SBOM)审计项

SBOM 增强示例

{
  "component": "@anthropic-ai/claude-code",
  "version": "2.1.88",
  "properties": [
    {
      "name": "security:source-map-exposure",
      "value": "critical - ~60MB map file with sourcesContent and R2 URL detected in tarball"
    },
    {
      "name": "security:remediation",
      "value": "upgrade to >=2.1.89"
    }
  ]
}

CI/CD 安全加固清单

  • 使用 commit SHA 固定工作流版本(非 tag/branch),防止供应链投毒
  • Artifact 上传白名单化,禁止上传包含环境变量或 .git/config 的文件
  • 实施最小权限原则,CI token 仅授予只读权限
  • 集成 DAST(动态应用安全测试),扫描运行时 source map 暴露

4.3 组织层:建立安全文化与问责机制

培训与意识

  • 将 source map 泄露案例纳入开发人员安全培训(重点讲解 Anthropic 事件)
  • 明确告知:现代构建工具默认配置不安全,必须显式禁用 source map
  • 每季度进行"构建安全"专项审计

事件响应预案

1. 发现泄露 → 立即从 npm/CDN 移除含 map 的包版本,标记废弃
2. 影响评估 → 检查 map 文件中是否包含硬编码密钥、内部 API 端点、云存储 URL
3. 密钥轮换 → 若 map 中含密钥或存储 URL,立即轮换所有相关凭证
4. 代码审计 → 基于泄露代码进行安全审计(假设攻击者也会这么做)
5. 事后复盘 → 更新构建规范,防止复发,纳入团队绩效考核

度量与 KPI

  • 将"source map 泄露事件数"纳入安全团队 OKR(目标:0 起)
  • 定期扫描生产环境,统计暴露 source map 的应用占比(目标:0%)
  • 对重复犯错的团队实施构建权限管控(需安全团队审批发布)

4.4 战略层:从"防泄露"到"防利用"的纵深防御

即使发生泄露,也要降低攻击者利用价值:

代码层防护

  • 密钥管理: 使用 AWS Secrets Manager、HashiCorp Vault,禁止硬编码
  • API 设计: 实施零信任架构,所有端点需认证授权,不依赖"隐藏"保安全
  • 混淆加固: 使用 JavaScript 混淆工具(如 javascript-obfuscator)增加逆向难度

架构层防护

  • 网络分段: 前端静态资源与后端 API 分离部署,限制跨域策略
  • WAF 规则: 拦截异常 API 调用模式(攻击者通过源码发现的隐藏端点)
  • RASP: 运行时应用自我保护,检测基于泄露代码的针对性攻击

AI 加速的逆向工程

随着 AI 编码助手的普及,攻击者将泄露的 Source Map 输入 AI,可在几分钟内完成原本需要数小时的代码逻辑梳理与漏洞挖掘。这使得 Source Map 泄露的危害性从"代码暴露"升级为"即时可用的攻击知识库"。防御措施需同步升级:在代码中增加结构化注释混淆、避免使用易于 AI 解析的标准化模式,并假设泄露代码会被立即用于自动化漏洞挖掘。

4.5 供应链延伸:第三方依赖的 source map 审计

问题场景

  • 依赖 A 的 npm 包包含 dist/index.js.map
  • 依赖 B 的 GitHub Release 附带 source map

管控措施

# 安装后扫描所有依赖的 source map
find node_modules -name "*.map" -type f -exec ls -lh {} \;

# 统计含 source map 的依赖包
find node_modules -name "*.map" -type f | awk -F'node_modules/' '{print $2}' | awk -F'/' '{print $1}' | sort | uniq -c | sort -rn

# 使用 Snyk/Black Duck 等 SCA 工具检测
snyk test --json | jq '.vulnerabilities[] | select(.title | contains("source map"))'

企业策略

  • 建立"禁止 source map 依赖清单",定期审计 Top 20 核心依赖
  • 对含 source map 的依赖,要求供应商提供"无 map 版本"或自行构建过滤
  • 在内部 Artifactory/Nexus 中配置上传策略,自动拦截含 .map 的包

4.6 云存储场景的补充管控

对于使用 OSS/CDN 托管前端资源的场景:

阿里云 OSS 策略示例

{
  "Statement": [
    {
      "Effect": "Deny",
      "Action": ["oss:GetObject"],
      "Resource": ["acs:oss:*:*:your-bucket/*.map"],
      "Principal": "*"
    }
  ]
}

CDN 配置建议

  • 禁止缓存 .map 文件(避免通过 CDN 预热泄露)
  • 配置 URL 鉴权,要求携带有效 token 才能访问
  • 定期使用 CDN 日志分析异常 .map 文件请求

结语:安全是细节的艺术

Anthropic Claude Code 泄露事件给我们最深刻的教训是:安全漏洞往往藏在最不起眼的配置项中。一个未正确设置的 sourcemap = "external",一次遗漏的 .npmignore 更新,就能让价值数亿美元的知识产权瞬间裸奔。

截至发稿时,Anthropic 已确认"无客户数据或模型权重泄露",正在推出预防措施防止复发。社区已出现自动化检测工具,用于扫描其他 npm 包的 source map 暴露情况,部分企业也开始将"source map 暴露"纳入漏洞赏金计划范围。

对于安全从业者,这提醒我们:供应链安全不能只关注依赖漏洞(CVE),更要关注构建产物泄露这类"非传统"风险。对于管理层,这证明了安全左移的必要性——在 IDE 插件、构建模板、CI 模板中内置安全策略,比事后审计更有效。

最后,记住这个简单的检查清单:

# 发布前运行
npm pack --dry-run | grep ".map" && echo "❌ FAILED" || echo "✅ PASSED"

# 构建配置确认
cat bunfig.toml | grep sourcemap  # 应为 false 或 none

# 部署后验证
curl -I https://your-site.com/app.js.map  # 应返回 404 或 403

# Docker 镜像验证
docker run --rm your-image find /app -name "*.map"

安全无小事,配置即边界。


参考资源


初稿:Kimi
二审:DeepSeek
三审:Kimi
四审:DeepSeek
终版:人工调整

posted @ 2026-04-01 08:54  M4K0  阅读(38)  评论(0)    收藏  举报