先看一个场景。你在做一个 SaaS:后端连着云数据库,前端是 Next.js,迭代节奏很快。上线前请安全团队做了一次黑盒测试,第三天报告回来,两个高危:
- C1:首页「查看源代码」,
__NEXT_DATA__那段 JSON 里躺着本应只存在于服务器端的完整后台配置——数据库地址、消息队列端点、内部服务地址、签名密钥。 - C2:前端产物里出现了 secp256k1 相关的密钥材料。于是有人开始查链上余额。
这里先停一下,注意 C2 的措辞:「密钥材料」。一个 hex 字符串长得像私钥,不等于它就是私钥——它可能是公钥,可能是演示值,可能是早就作废的测试密钥。但这两个发现指向同一个更根本的问题:开发者没有意识到「服务器知道的数据」和「浏览器可以获得的数据」之间,必须有一道刻意设计的边界。 本文想讲清楚三件事:这道边界在哪里;为什么它会无声无息地消失;在 AI 写代码的时代,它为什么消失得越来越快。
(说明:开头场景对测试报告细节做了揉合改写,不指向任何具体产品;全文只讲原理、风险与防御设计,不提供针对真实系统的攻击步骤。)
一、浏览器到底能看到什么?
先建立一条最容易被遗忘的基线:浏览器的使用者,不是你的同事。 浏览器运行在攻击者自己的设备上,他们拥有完整的时间、工具和动机去检查你的应用送来的每一个字节。
能做的事情包括:打开 DevTools 看 Network 面板里每一个请求与响应;用命令行直接抓取 HTML 和 JSON;读 localStorage、sessionStorage、IndexedDB 和 Cookie;把压缩过的 JavaScript 格式化;对混淆过的代码做自动反混淆——开源工具 webcrack 这类项目已经把「还原混淆代码」做成了成熟工作流,安全厂商的工程博客里也不乏完整的去混淆战法。MITRE 的弱点库把「依赖混淆和隐藏来当安全措施」单独列为 CWE-656,就因为它太常见。
所以规则只有一句:只要数据到达了浏览器,它就不再是服务器秘密。 前端是公共场所,不是保险箱。你后面会看到,几乎所有泄露都源于对这句话的无视。
二、Next.js 的 NEXT_DATA 到底是什么?
__NEXT_DATA__ 不是漏洞,它是一个设计如此的数据通道。在 Next.js 的 Pages Router 架构下,每个服务端渲染的页面 HTML 里都有一段:
| |
这段 JSON 会序列化 getServerSideProps、getStaticProps、getInitialProps 返回给页面的 props,连同路由、query、buildId 与若干渲染标志(旧版还有 publicRuntimeConfig,已随 Next 16 移除)一起放进 DOM。它为什么存在?为了保证 hydration 一致:服务端预渲染出可见的 HTML 后,浏览器里的 React 必须用同一份 props 重算同一棵组件树,否则页面会闪烁、会不匹配——而把 props 装进 DOM 里的 JSON script 是最自然的载体。这是框架维护者在官方仓库 discussion(#15117)里解释过的机制性理由,不是失误。
问题在于它的原料。官方 getServerSideProps 文档写得明明白白:传给页面组件的 props「会作为初始 HTML 的一部分对客户端可见」,并明确要求不要把任何不该到客户端的数据放进 props。但这句话的约束力约等于餐厅门口的伞架:顺手的人太多。常见的写法是把整条数据库记录、整个配置对象直接 spread 进 props:
| |
于是同一个 secret 会出现三份副本:烤进可见 HTML 的文本、__NEXT_DATA__ JSON 块、以及客户端导航时返回 JSON 的 /_next/data/<buildId>/<path>.json 端点(官方文档确认客户端导航就是走这个 API 请求重新拿 props)。如果页面是静态生成(SSG)的,props 还会被烤进静态文件,由 CDN 以长达一年的缓存时间(s-maxage=31536000,官方 CDN 缓存文档)对外分发——一次构建期的失误,会在 CDN 上原地躺满一年。

App Router 时代机制换了载体,边界问题不变。Pages 风格的 __NEXT_DATA__ 被 RSC(React Server Components)Payload 取代:渲染结果以 React Flight 序列化格式流式塞进页面(实现上表现为 self.__next_f.push(...) 的内联脚本,属实现细节),客户端导航则复用普通 URL 加 ?_rsc= 参数。关键在于:凡是传给 Client Component 的 props,都必须跨网络序列化——官方「数据安全」指南里直接给了一个反面示例,注释写着「EXPOSED: This exposes all the fields in userData to the client」,并把它定性为与前代同级的反模式。Server Actions 同理:官方文档写明「导出的 action 就是公开端点」,可以被直接 POST 调用;Next 16 虽加了加密的 secure action IDs,官方数据安全指南仍提醒:不要把 closure 加密当成对客户端隐藏敏感值的手段。2025 年的 CVE-2025-29927(middleware 绕过)则提醒了另一面:连框架自己的边界机制都可能被绕过,鉴权必须落在每个端点内部,而不是中间件的单点检查上。
框架也给了顺手的防御件:import 'server-only' 会让任何客户端环境引入该模块时直接构建报错;官方推荐的技巧还包括把数据包装成 class 再传——因为函数与 class 实例无法被 Flight 序列化,等于用类型系统当最后的栅栏。至于 Taint API(taintObjectReference/taintUniqueValue),截至 2026 年 8 月的 Next 16.3.4 官方文档仍标为 experimental,并提示别把 Taint API 当作防止敏感数据下发客户端的唯一防线——可以当兜底,不能当地基。
三、为什么配置会「意外」泄露到前端?
黑盒报告里的 C1 不是特例,它通常由四条路径之一造成:
1. 整包配置 spread 进 props。 图省事,把 config、runtimeConfig、settings 整体塞给页面组件,「反正前端用到的字段我再挑」。但序列化发生在整包上——前端挑不挑,数据已经出去了。
2. 环境变量前缀的直觉陷阱。 Next.js 的规则是:带 NEXT_PUBLIC_ 前缀的变量,会在构建期被内联进发给浏览器的 JS bundle(底层是 webpack 系 DefinePlugin 的纯文本替换);不带前缀的变量只在 Node 服务端可用,在客户端模块里引用会得到空字符串。这里有个反直觉的点:NEXT_PUBLIC_ 名字里有「PUBLIC」,它恰恰是把值变成公开的开关,而不是「公开使用变量」的开关。Vite 的 VITE_、CRA 的 REACT_APP_ 是同一套逻辑,且官方文档都在劝退:Vite 原文建议「为避免意外泄露环境变量到客户端,不要使用这个前缀」,CRA 文档的警告更直白——「不要在 React 应用里放任何秘密,环境变量被嵌入构建产物,任何人查看应用文件都能看到」。
还有两个连锁效应容易被忽略:内联发生在构建期,值从此冻结——部署后再改环境变量不影响线上 bundle;如果泄露后只改了 Secret Manager 里的值而不做完整重新构建、不清 CDN 缓存,旧值会继续在线上服务。
3. 把整行记录传给 Client Component。 App Router 里 Server Component 查库后把整条 userData 甩给 Client Component——这是官方数据安全指南点名的最常见反模式,注释里标着 EXPOSED。
4. 环境变量的版本链没想清楚。 .env 有优先级链(shell 里已有的 > .env.$(NODE_ENV).local > .env.local > .env.$(NODE_ENV) > .env),一个变量到底来自哪一层、会不会被进容器镜像,很多团队没人能答上来。
注意前三条的共同病灶:它们都是「让代码先跑起来」的最短路径。这一点在第六部分会变得重要。
四、从 API Key 到 secp256k1 私钥
把泄露的东西分个级,能说清楚 C2 的严重性。行业里两个官方分级值得直接照搬:
Stripe 的官方口径:pk_ 开头的 publishable key(可公开密钥),文档标注「Safe to expose: Yes」——它只能标识账户、把支付信息换成 token,「无法执行创建扣款或读取账户数据等敏感操作」;sk_ 开头的 secret key(秘密密钥),标注「Safe to expose: No」,持有全部权限。Firebase 官方文档有同样的双层表述:在正确施加 API 限制的前提下,「限定于 Firebase 服务的 API key 不需要当作秘密」,真正的访问控制由 Security Rules 和 App Check 承担。一句话总结这套分级:可公开的材料(公钥、地址、publishable key、前端配置)只承担「标识」职能;机密材料(私钥、secret key、助记词)承担「授权」职能。 授权职能的东西越过浏览器边界,性质就变了。
secp256k1 是 SECG 标准组织 SEC 2 标准里的一条椭圆曲线——曲线参数本身是公开的,它出名是因为比特币在实现阶段选它做 ECDSA(椭圆曲线数字签名算法)交易签名(白皮书只描述使用数字签名,曲线是 2009 年实现时选定、2010 年随标准落定),以太坊沿用同一曲线。它最关键的属性是点乘单向性:用私钥可以轻松推导公钥,反过来在密码学上不可行——这正是 ethereum.org 文档里的原话逻辑。以太坊地址就是「公钥 Keccak-256 哈希的最后 20 字节加 0x」。再深一层:用户手里该持有私钥的唯一正当场景,是密钥属于用户自己——比如 MetaMask 这类非托管钱包,助记词只留在用户设备的加密 vault 里,服务方从不接触。而「网站运营者把自己的签名私钥放进自己的前端」,是另一个物种:那相当于把 sk_ 打印在门口发给每个访客。
「如果暴露的真是私钥」,位置决定后果的形态:
- 打包进 JS bundle:供应链事件已演示过钥匙进了 bundle 会怎样——2018 年 npm 包 event-stream 被接管,注入的恶意代码专门窃取集成它的 Copay 钱包私钥;2024 年 12 月两个伪造的
@solana/web3.js版本把函数替换成窃取私钥外传的代码。你 bundle 里的每一行,都可能被依赖链里任何一环读取。 - localStorage / IndexedDB:浏览器里的持久化存储是 XSS 和木马的第一目标。2023 年首现的 AMOS 木马就明确把 MetaMask、Phantom 等钱包扩展列为窃取对象——合法钱包尚且如此,自制存储更不能幸免。
- 前端配置 / 注入脚本:BadgerDAO 2021 年因攻击者拿到其 Cloudflare 密钥、向前端注入脚本骗取用户授权交易而损失约 1.2 亿美元(微软把这类攻击命名为 ice phishing);2023 年 MyAlgo 网页钱包被注入恶意 JS,损失约 920 万美元。前端配置失守 = 等号右边是用户的全部授权。
- API Response / 服务端遥测:2022 年 Slope 钱包把用户助记词发进了自家 Sentry,9,231 个地址被盗约 410 万美元;同年 3Commas 在平台侧泄露约 10 万条用户交易所 API key。密钥一旦进入响应或日志管道,就没有收回的可能。
- Source Map:2026 年 3 月 31 日,Anthropic 发布的 Claude Code npm 包意外附带
cli.js.map,sourcesContent内嵌了约 51 万行完整专有源码,任何人可一键还原——source map 会把 minify 提供的「不可读性」直接归零,藏在里面的任何常量一并见光。 - 作为边界参照:服务方签名私钥失守的极端后果,Harmony Horizon 跨链桥 2022 年给出了答案——验证者私钥被攻破,约 1 亿美元资产被清空。服务方私钥不需要任何前端参与,本身就是完整的地雷。
现在回到 C2 的措辞纪律:黑盒报告里看到的 secp256k1 材料,「可能涉及」真实私钥,「如果暴露的是私钥」,上述后果全部适用;但它同样可能只是一个公钥、一个测试向量、或者一段早已作废的演示值——这「需要进一步验证」(验证方向是:能否由它推导出与线上资产对应的公钥和地址、它的来源位置与生命周期)。真正站得住脚的结论不是「这里泄露了一把具体的私钥」,而是「这个应用的架构没有做任何密钥分级,导致密钥形态的材料出现在了它不该出现的位置」。 混淆不提供保护——minify、字符串加密、自研「客户端加密」都只是把锁和钥匙一起交给对手(CWE-656 的变体);凡是能在 DevTools 里运行的代码,对手都有能力分析它。
五、现代 Web 应用的秘密泄露面:一张八层地图
把散落的点串起来,现代 Web 应用的秘密泄露面大致有八层。每一层都有官方定义或公开事件背书。
第 1 层:前端 Bundle。 硬编码的 key 明文出现在每个访问者可下载的文件里;NEXT_PUBLIC_/VITE_/REACT_APP_ 前缀让环境变量在构建期变成明文字面量;AWS 长期密钥的 AKIA 开头、私钥的 -----BEGIN 开头、sk_ 前缀,都是可被机器扫描的稳定特征。检测端开源工具链已经成熟:gitleaks / trufflehog 扫仓库与产物,semgrep 有现成的 hardcoded-jwt-secret 规则。注意扫描对象必须是构建输出,光扫源码会漏掉内联结果。
第 2 层:环境变量。 前缀语义(公开与否)、构建期冻结、轮换后旧值滞留,第三部分已述。额外的一条:.env.local 等文件名为「local」,描述的可能不是「本地调试用」,而是「掉进 git 的风险」。
第 3 层:API 过度暴露。 OWASP API Top 10 2019 把 Excessive Data Exposure(过度数据暴露)定义为 API「按设计就向客户端返回敏感数据,通常在客户端渲染前才被过滤」,官方给的防御第一条是「绝不依赖客户端来过滤敏感数据」。官方示例极其贴切:一个监控 API 返回全部摄像头清单和每个摄像头的 live_access_token——「保安的界面只显示授权建筑」,但接口返回的是全部。2023 版把这条吸收进 API3 Broken Object Property Level Authorization(合并了过度暴露和 Mass Assignment),把焦点从症状转到根因:对象属性级授权缺失。另一个相关大类 BOLA(对象级授权失效,Broken Object Level Authorization,API1;大众熟知的 IDOR「不安全直接对象引用」是它的典型形态)说的是改个 ID 就能读别人对象的问题。这类问题的现实样板是 Venmo:2018 年起研究者通过其公开 API 下载了 2017 全年约 2.08 亿条交易记录并建站展示,教训就是那句——App 界面里的「隐私设置」是客户端过滤,数据层的 API 依然默认公开。 ASVS 5.0(2025 年 5 月发布)把底线写成了一条 L2 要求原文:机密「不得包含在应用源代码或构建产物中」(Secrets must not be included in application source code or included in build artifacts)。
第 4 层:Source Map。 ECMA-426 标准规定了 .map 文件的 sourcesContent 字段——存在它时,完整原始源码内嵌在公开的 map 文件里;sources 数组还常常带出服务器内部绝对路径甚至开发机用户名。三个要命的误解:Vite 的 hidden 模式只是删掉引用注释,map 文件照常生成;Next.js 开启 productionBrowserSourceMaps 后官方文档明说会自动对外提供这些文件;「已上传 Sentry 但没公开引用」不等于安全——Vercel 官方 Conformance 规则 NEXTJS_NO_PRODUCTION_SOURCE_MAPS 的措辞是开启生产 source map 等于「公开分享你的应用源代码」,官方推荐流程是上传错误追踪平台后清空再部署。真实代价有据可查:Sentry 安全团队 2025 年的案例里,攻击者从公开 map 还原出未文档化的改密接口完成账号接管;同年案例还有研究员从生产 map 里还原出硬编码的 Stripe secret key。
第 5 层:浏览器存储。 localStorage / sessionStorage / IndexedDB / Cookie。一个简单的决策树:需要由 JavaScript 读取的敏感值(令牌、密钥)放哪里都会和 XSS 共享命运;会话凭证应当走 httpOnly Cookie;「秘密」这一类根本不配进浏览器存储。钱包扩展自己也要用加密 vault 保护数据——存储位置本身不提供任何隔离。
第 6 层:Git。 历史是永远的。GitHub 官方「移除敏感数据」文档的态度很清醒:进入仓库那一刻就默认受污染,第一步永远是 revoke/rotate 密钥,删历史只是补救——fork、同事的 clone、缓存的视图都还在。公开事件链条完整:Uber 2016 年因 GitHub 上的 AWS 密钥被下载 S3 备份库,波及 5700 万用户,最终 1.48 亿美元和解、时任 CSO 因掩盖被定罪;丰田 T-Connect 2017 年 12 月把含访问密钥的源码推上 GitHub,2022 年 9 月才发现,29.6 万名客户信息暴露风险。规模数字来自 GitGuardian 2026 年报告:2024→2025 年公开 GitHub 新增泄露密钥 2865 万个、同比增长 34%,其中 AI 服务密钥增长 81%;2022 年验证过的泄露凭据到 2026 年初重测仍有 64% 有效——大多数人泄露后根本不轮换。
第 7 层:CI/CD。 GitHub 的 secret scanning 会扫全量历史、免费覆盖公开仓库,push protection 在推送前阻断。真正不兜底的是 CI 日志遮蔽这一步:官方明确,含空白的结构化值(JSON/XML/YAML)会「显著降低被正确遮蔽的概率」——而 CI 日志本身就会打印环境变量的值。CI 供应链是更高维的威胁:2025 年 3 月,23,000 多个仓库使用的 tj-actions/changed-files 被改 tag 植入后门、把 runner 内存里的密钥写进日志,CISA 为此发布警报(CVE-2025-30066);2021 年 Codecov 的 Bash Uploader 被篡改,把客户 CI 环境变量外传数月。
第 8 层:Docker 与云。 Docker 官方文档明确警告不要用 ARG 传密钥——构建参数会留在最终镜像元数据、provenance 与镜像历史里;正确姿势是 BuildKit secret mounts。实证数据:一项涵盖 337,171 个 Docker Hub 镜像与 8,076 个私有 registry 镜像的研究发现,总体上 8.5% 的镜像含密钥、共 52,107 个私钥(单看 Docker Hub 为 9.0%);Codecov 事故的起点正是攻击者从公开自托管镜像的历史层挖出 GCS 凭据。云侧同构:2017 年公开 S3 bucket 泄露潮(Accenture 等);Terraform 官方承认 state 是明文文件、sensitive 值照样写入;AWS 官方建议敏感凭据放 Secrets Manager 而非 Lambda 环境变量。

这张地图的坏消息是它闭合成了一个环:任何一层泄露的凭据,都能用来攻击其他七层——比如 BadgerDAO 2021 年正是被泄露的 Cloudflare key 注入前端脚本、钓走约 1.2 亿美元——一层失守,七层受攻。
六、AI 编程为什么让问题更严重
这是本文真正想讨论的部分。核心判断一句话:在 AI Agent 和 AI Coding 时代,最大的安全风险并不是攻击者变得更聪明,而是开发者制造和部署软件的速度,已经超过了他们理解安全边界的速度。
证据分三层摆。
第一层,代码质量本身。 斯坦福等机构发表于 ACM CCS 2023 的用户研究(Perry 等)是首个大规模实证:使用 AI 助手的参与者写出了显著更不安全的代码,同时更倾向于相信自己写的是安全的——过度自信是不安全代码的放大器。对照组研究(USENIX Security 2023 的 Lost at C)在写 C 的场景得出了相反的温和结论,说明这不是宿命,而是条件相关的风险——但厂商的持续测试显示风险普遍存在:Veracode 2025 年 GenAI 报告对多个模型生成的 Java/Python/C#/JavaScript 测试中,45% 的样本未通过安全测试并引入 OWASP Top 10 级漏洞,Java 失败率高达 72%。更麻烦的是感知缺口:Snyk 2024 年调查里,超过 75% 的开发者认为 AI 写的代码比人更安全,同时 56% 承认 AI 生成代码有时或经常引入安全问题;只有约 10% 的开发者会扫描大部分 AI 生成的代码。GitClear 基于 2.11 亿行变更的分析则显示结构性下滑:克隆/复制粘贴占比从 2021 年的 8.3% 升到 2024 年的 12.3%,历史上首次「复制粘贴」行数超过「重构移动」行数。复制粘贴债不加密钥,但它让「哪里出现过密钥」的审计也变得无从下手。
第二层,秘密专项数据。 GitGuardian 2026 报告给出了一条直接相关的新数据:Claude Code 辅助的提交,密钥泄露率 3.2%,是全部公开提交基线 1.5% 的两倍多。 机制可以推演(以下行为模式属安全界的通行观察,尚无一一对应的公开案例):AI 的目标函数是「让代码跑起来」,而泄露发生在未来、在别人的成本表上,生成时没有任何疼痛信号;训练语料里常见写法之一就是「把 key 放在方便调试的位置」;而 Agent 拥有读权限时,.env 这类文件就在它的手边。2025 年 Hacker News 上那个「vibe coding 应用因 API key 泄露损失 300 美元」的帖子,以及安全厂商记录过的「被迫关停重写」案例,是这条机制的个人尺度样本。vibe coding 这个词本身是 Karpathy 在 2025 年 2 月创造的(「完全交给 vibe、拥抱指数增长,甚至忘了代码的存在」,意译),安全界的批评随后而至,ReversingLabs 的总结最尖锐:AI 生成的代码量已经超过人工审查的能力,而 vibe 开发者不读代码、不写测试,漏洞与恶意包直通生产。
第三层,Agent 本体。 当你把文件系统、Shell、Git、云端、网络的权限交给一个会自主行动的 Agent,泄露面就从「代码写得差」升级为「系统被漫游」。标准层面,OWASP LLM Top 10(2025)里 LLM05(LLM 输出不当处理)与 LLM06(Agent 被授予过度自主权)直接命中这一档,OWASP 2025 年 2 月发布了《Agentic AI – Threats and Mitigations》,2025 年 12 月又发布了 2026 版 Agentic Applications 十项(发布稿点名三项:Agent Behavior Hijacking、Tool Misuse and Exploitation、Identity and Privilege Abuse)。工具链层面,MCP 的官方规范自己写明是「隐式信任」模型并要求客户端把 MCP 服务器视为不可信输入源;Invariant Labs 2025 年 4 月的实测是这条路线的注脚——恶意 MCP 工具描述引导 Cursor agent 读走了 ~/.ssh/id_rsa。代码 Agent 本身也会被攻破:Claude Code 先后修复了 DNS 外泄数据(CVE-2025-55284)与恶意仓库配置导致的 RCE/密钥窃取(CVE-2025-59536,CVSS 通用漏洞评分 8.7)。

落到权限模型的正面答案其实各家都在做:Claude Code 官方安全文档的结构是 Manual 模式默认只读、危险命令由分类器代审、提供文件系统+网络沙箱、工作目录有边界,并反复强调一句话——「Claude Code 只拥有你授予它的权限」;Copilot 的 agent mode 嵌在 workspace trust 体系里;Replit 甚至推出了「安全 vibe coding」,在 AI 生成时就扫描和拦截密钥。防灾的杠杆不在模型,在授予权限的那只手上。
回到开头那句「顺手」。AI 不一定黑进你的系统——它可能只是用最快路径帮你完成任务,而最快路径常常长这样:把服务端配置塞进 pageProps 让页面少报一个错;给 key 加个 NEXT_PUBLIC_ 前缀免得构建失败;临时关掉鉴权跑通流程,再把关掉的鉴权一起提交上生产。 它没有恶意,它只是不知道边界在哪——而知道边界本该是「写代码的人」的职责。当写代码的速度超过了理解边界的速度,安全债务的利息就从「偶尔一次事故」变成了「结构性的基线泄露」。
七、真正安全的 Secret Management 架构
正确的形态和错误的形态,各是一条链。
错误链(前面所有案例都在这条链上):
| |
正确链:
| |
正确的链上有六个结构性要求,每一项都有官方出处:
- Secret Manager:ASVS(应用安全验证标准)4.0.3 的 6.4.1 与 5.0 的 13.3.1(内容对应、非官方映射)的密钥库要求;AWS Secrets Manager、GCP Secret Manager、Azure Key Vault 各自的最佳实践(最小权限、版本引用、审计日志)都指向同一件事——密钥的创建、存储、访问、销毁都要有独立于应用的载体。
- 环境隔离:每个环境独立 vault、爆炸半径最小(Azure 官方最佳实践原文思路);生产凭据永远不进入开发环境与 Agent 的工作上下文。
- Least Privilege:按服务最小授权,尽量用能轮换的短时凭据、workload identity 替代长期 key。Stripe 新版引导的
rk_Restricted Key 代表了行业方向:把「无限权限密钥」从默认选项中删掉。 - 服务端签名:一切签名、加密、持有私钥的操作留在服务端(BFF 面向前端的后端 / 接口路由);浏览器拿会话(httpOnly Cookie)或一次性的用户授权,而不是拿能长期签名的材料。secp256k1 的私钥永远没有去前端的差旅费。
- Key Rotation:轮换是一等公民,不是事故后的补救。AWS 官方泄露处置博客的顺序是:先评估凭据可达范围,随即 deactivate(禁用而非删除,禁用可恢复);GitHub 官方口径同样是泄露即 revoke/rotate 优先于清理历史——密钥一旦作废,历史里的密文就是无害的。
- Secret Scanning 前置:push protection + gitleaks pre-commit + CI 全量扫描 + 对构建产物(bundle、镜像、map 文件)的扫描,构成四道闸。再加上响应最小化:API 返回只含必要字段的 DTO(数据传输对象)而非整行(OWASP EDE 的「绝不依赖客户端过滤」)、schema 校验响应。
一个可以在团队里立住的验收句:「服务器知道的数据」与「浏览器可以获得的数据」必须是两个有意设计的集合;任何数据跨越这条线,都要有被写下来的理由和类型。 没有理由的跨越就是 C1,没有类型的跨越就是 C2。
八、给独立开发者的安全检查清单
这份清单每一条都对应上文的一层,按「先止血后加固」排序;以下检查全部针对你自己的应用与资产——对任何非自有、未获授权的目标执行即越界:
- □ 查 Git 历史:
gitleaks detect --all或 trufflehog 扫全历史;命中即先 revoke/rotate,再考虑清历史 - □ 查公开仓库:确认仓库非公开;打开 GitHub push protection 和 secret scanning 告警
- □ 查构建产物:对 dist/build 目录扫
NEXT_PUBLIC_、VITE_、REACT_APP_、AKIA、-----BEGIN、sk_等特征 - □ 查页面源代码:view-source 首页,检查
__NEXT_DATA__JSON 块与 RSC payload 里有没有不该出现的东西 - □ 查 /_next/data 与 API 响应:接口是否返回整条对象?字段是否超过页面所需?
- □ 查 Source Map:生产是否禁用了 map;若已上传 Sentry,部署产物里是否清空了
.map - □ 查浏览器存储:localStorage/sessionStorage 里有没有 key、token、密文;会话走 httpOnly Cookie
- □ 查 CI/CD 日志:日志里有没有被打印的密钥;敏感步骤的输出是否遮蔽
- □ 查 Docker 镜像:
docker history与公开 registry 的镜像层;构建参数是否用 BuildKit secret mounts - □ 查环境变量链:一个
X变量最终值来自哪一层.env——谁能答上来 - □ 查 AI 生成代码的闸门:生成即扫描(semgrep/gitleaks 入 CI),生成即审查,项目规则文件里写明密钥边界
- □ 查 Agent 权限:Agent 的 shell、网络、云凭据是否按最小集合授予;敏感项目在沙箱/容器里跑
收尾
如果把这篇东西压成一句话:前端安全的第一课不是「防注入」,而是先搞清哪些字节会被送到浏览器。过去这一课靠代码审查把关,现在的量产代码来自一个不在乎这个过程是否安全的生成器——这不是拒绝 AI 的理由,而是必须把安全闸门从「写代码的时候」挪到「代码进入生产之前」的理由。
审查可以不跟生成一样快,但至少不能缺席。
参考来源
- Next.js 官方文档:getServerSideProps、Environment Variables、Server and Client Components、Data Security Guide、CDN Caching、productionBrowserSourceMaps、Taint API
- React 官方:Server Functions / experimental_taintObjectReference;ECMA-426 Source Maps 规范(tc39.es)
- OWASP:API Security Top 10(2019/2023)、Web Top 10 2021、ASVS 4.0.3 与 5.0、WSTG-INFO-05、LLM Top 10 2025、Agentic AI Threats & Mitigations、MCP Security Cheat Sheet
- Stripe 官方 Keys 文档(含 Restricted Keys rk_ 一节);Google Firebase 官方 API Keys 文档;ethereum.org Accounts 文档;Vite / CRA / webpack 官方配置文档
- GitHub 官方:secret scanning、push protection、Security hardening for GitHub Actions、Removing sensitive data;2024-02-29 官方博客(前八周超 100 万泄露密钥)
- GitGuardian《State of Secrets Sprawl 2026》;Veracode《2025 GenAI Code Security Report》;Snyk《AI Code Security Report 2024》;GitClear《AI Copilot Code Quality 2025》
- Perry et al., “Do Users Write More Insecure Code with AI Assistants?”, ACM CCS 2023;Dahlmanns et al., Docker Hub 密钥实证研究 2023
- Sentry 安全博客《Abusing Exposed Sourcemaps》(2025-01);Vercel Conformance NEXTJS_NO_PRODUCTION_SOURCE_MAPS
- 事件报道:Uber 2016(The Verge)、丰田 T-Connect 2022、CircleCI 2023 事故报告、Codecov 2021、tj-actions 2025(CISA)、event-stream 2018、@solana/web3.js 2024、Slope 2022(Solana 官方)、BadgerDAO 2021、MyAlgo 2023、Harmony 2022、Claude Code npm source-map 2026、Venmo 公开 API(TechCrunch 2019)
- Invariant Labs MCP 安全系列(2025-04);Claude Code 官方安全文档;Check Point CVE-2025-59536 分析;CVE-2025-29927 Vercel 复盘
- EmbracetheRed CVE-2025-55284 披露;VS Code 官方博客 Copilot agent mode(2025-02-24);ReversingLabs《Vibe coding: What automating development means for AppSec》;Aikido vibe coding 安全清单;Hacker News《Lost $300 due to an API key leak from “vibe coding”》;Replit《Safe vibe coding》;Karpathy vibe coding 词源(Wikipedia)
