前端代码是不是安全边界?这个问题的答案,我在一次线上事故里被狠狠教育过。那次后端同事反馈:“部署和本地代码一样,但是前端返回内容不一样。”我们的 Vue3 项目在本地跑得毫无问题,登录、列表、详情全都正常,推到测试环境后,一连串接口开始 403,部分页面直接白屏。刚开始所有人都以为是环境变量配错了,我对着 Nginx 配置和.env.production排查了一下午,最后在浏览器 Network 面板里对比线上返回的 JS chunk 才意识到问题没那么简单——线上产物里居然完整保留着内部接口域名、调试开关、甚至一个被注释掉的“仅限内网”的后端网关地址。更麻烦的是,某个所谓“不可见”的管理端接口,后端压根没做权限校验,它只靠“前端代码里没有入口”来伪装隐蔽。
这件事让我彻底明白,前端代码泄露从来不是“源码被扒”这么简单。它是一整套后端权限体系的隐形黑洞——你以为藏住了,其实在全网裸奔。这篇文章我想把前端代码泄露的根源、常见泄露渠道、以及防护体系应该怎么重构,完整梳理一遍,尤其适合正在维护前后端分离项目、使用 Jenkins 部署、或者刚接触 Vue3 和低代码平台的朋友。不整虚的,都是实战里踩出来的结论。
1. 从一次“部署后前端返回内容不一致”的排查,挖出的权限漏洞
1.1 先复盘那次事故:前端“看起来一样”,线上却不一样
项目是经典的前后端分离:Vue3 前端 + Python FastAPI 后端。我在本地通过 Vite dev server 代理/api,后端接口地址指向http://localhost:8000,一切正常。部署到测试服务器后,前端静态资源由 Nginx 托管,接口地址应该指向https://api.test.example.com。正常来说,只要.env.production里的变量没问题,打包产物就会用正确的接口地址。
可问题就出在:本地和线上代码完全一致,但浏览器拿到的 JS 内容里,接口前缀确实是对的,可多出来一段本地根本不会执行的逻辑——它包含一组用于“灰度环境”的内部 API 列表。这个列表在打包时被某种条件注入到了 main chunk 里,而本地开发环境因为 dev server 注入的 mode 不同,压根不会出现这段代码。所以本地复现不了,部署到线上就“内容不一样”。
这已经够让人头大了,真正让我后背发凉的是后续:我随手打开线上 JS 文件搜索api,发现好几个/admin/开头的内部接口直接明文暴露在里面。这几个接口在后端没有任何鉴权中间件,因为当初设计的人认为“前端没入口,别人就找不到”。但实际上,只要知道接口路径,用 curl 就能直接改数据。
1.2 为什么前端代码泄露会成为“后端权限的隐形黑洞”
很多人有个根深蒂固的误解:前端代码混淆了、压缩了、或者不公开仓库地址,就等于安全。实际上,前端代码对任何能打开浏览器的人都是完全透明的。只要用户能访问你的页面,他就能拿到全部 JS、CSS、HTML,能打开 DevTools 看 Network 请求,能断点调试。
这意味着什么?意味着你放在前端代码里的以下内容,等于公之于众:
- 所有 API 路径和参数结构
- 请求头里自定义字段的拼接方式
- 加密或签名的算法逻辑
- 内网 IP、域名、第三方服务的 key
- 后端接口的响应结构、状态码含义
- 被注释掉的调试信息、维护入口
- 低代码平台的权限配置、按钮级权限点
攻击者拿到这些信息后,能做的事远远超出“读懂你的业务逻辑”。他可以绕过前端界面,直接对后端接口发起批量请求。如果后端还天真地认为“这个接口没有前端入口,所以不需要鉴权”,那这个洞就是致命的。
我见过太多团队把“安全”押注在前端不可见上:接口不加鉴权、密钥硬编码、管理员路由靠前端跳转隐藏、按钮权限靠前端v-if控制。这些操作在前端代码泄露面前,全都不堪一击。我那次事故的根因也在于此——后端有两套接口,一套面向普通用户,一套“内部使用”,后者连最基本的 token 校验都没有。
1.3 “前端返回内容不一样”背后,往往是权限边界不清晰
后来我仔细想了想“部署和本地一样但返回内容不一样”这个现象,除了环境变量、构建插件、灰度逻辑之外,最常见的原因其实是:后端在不同环境下对接口的权限策略不同。
比如本地开发时,后端可能关闭了鉴权,方便调试;测试环境里,某些接口只校验来源 IP;生产环境则需要完整登录态。一旦前端代码里暴露了接口路径,攻击者就可能在本地模拟请求,逐个环境测试后端权限策略,找到最薄弱的那个入口。更麻烦的是,有些后端框架的 CORS 配置、OPTIONS请求预检、会话 Cookie 设置,也会因为部署环境不同而变化。这些差异靠前端代码根本控制不了。
所以那次事故最终给我的教训是:前端和本地代码一样、部署后行为不一样,不是单纯的环境 bug,它往往暴露的是安全边界定义不清晰的问题。什么接口需要登录、什么接口需要角色权限、什么接口完全公开,应该在后端统一声明,而不是靠前端代码“藏”出来。
2. 浏览器里没有秘密:前端代码泄露到底漏的是什么
2.1 前端代码的“可读性”是天然属性
先说一个基本原则:只要代码在浏览器里执行,就没有真正意义上的秘密。你可以把变量名改得乱七八糟,可以把字符串拆成 Unicode 转义,可以把控制流打散成永不重复的回调地狱,但最终浏览器引擎必须能解析并执行它。攻击者不一定需要读懂你的源码——他只需要在运行时观察、断点、修改。
从这个角度看,前端代码泄露包括两个层面:
- 静态泄露:构建后的 JS 文件本身被下载、搜索、分析。
- 动态泄露:运行时通过 DevTools、代理抓包、内存读取等方式获取关键信息。
静态泄露主要影响源码逻辑、密钥、接口地址;动态泄露则连 token、用户信息、临时凭证都保不住。两者结合,基本等于把整套系统的运行手册交出去了。
2.2 哪些前端信息最容易成为攻击跳板
我把真实项目里常见的信息泄露点整理成了一张表,每次安全评审都对着看:
| 信息类型 | 典型例子 | 泄露后风险 |
|---|---|---|
| API 路径 | /admin/user/delete | 直接绕过界面操作后端数据 |
| 请求参数 | user_id,role,is_admin | 构造恶意请求,越权操作 |
| Token 存储方式 | localStorage.getItem('token') | 模拟登录态,身份冒充 |
| 第三方密钥 | 云存储 AK、地图 key、支付密钥 | 盗刷资源、产生费用 |
| 加密/签名逻辑 | MD5 加盐、HMAC 签名 | 伪造请求、篡改数据 |
| 内部域名 | 网关地址、跳板机 IP | 进一步扫描内网 |
| 调试开关 | isDebug=true | 开启隐藏功能、日志输出 |
| 权限点配置 | roleList、permissions | 定位后端鉴权盲区 |
很多人以为接口路径不是秘密,因为在前后端分离架构里,接口地址总会出现在 Network 面板。没错,接口地址出现在浏览器里没问题,问题在于后端有没有对每个接口做独立鉴权。前端代码泄露只是把这层含义放大了:当攻击者看到一个/admin/deleteUser接口,他会立刻去试这个接口是否需要 Cookie、是否需要 Header、参数怎么传。如果一个都不需要,那完蛋。
2.3 前端密钥为什么不能当“安全密钥”用
我经常看到有人在前端代码里写:
const accessKey = 'LTAI5t...' const secretKey = 'xxxxxxxxxx'然后安慰自己说“反正代码压缩了,人看不懂”。但实际上,这种密钥在浏览器里可以通过任何网络请求模块读取,也可以通过 DevTools 直接在 bundle 里搜到。攻击者一旦拿到,就能以你的身份调用云厂商 API,上传文件、发短信、扣费,甚至读取对象存储里的私密数据。
正确的做法是:所有需要保密的第三方凭证都必须由后端持有。前端需要调用某个云服务时,让后端生成一个短期、受限的临时凭证,再给前端使用。比如上传文件到 OSS,不是把 AccessKey 传给前端,而是让前端请求后端,后端调用 STS 服务签发临时 token,前端再用这个临时 token 直传 OSS。整个过程前端拿不到主密钥。
2.4 Python 与 Vue 的交互模型里,信任边界在哪里
既然聊到了前后端分离,就顺便说说 Python 与 Vue 最常见的交互方式。Vue 前端通过axios或fetch发 HTTP 请求到后端,Python 后端(常见的是 FastAPI、Flask、Django)接收请求后处理业务逻辑,再返回 JSON。
这个模型里,信任边界必须画在后端的入口处。前端发来的任何请求头、参数、甚至 Cookie,都不应该被后端无条件信任。尤其是这种代码模式:
@app.get("/api/user/info") async def get_user_info(request: Request): user_id = request.headers.get("X-User-Id") # 直接信任前端传的 user_id return get_user_detail(user_id)这就是典型的把权限判断交给了前端。攻击者只需要在请求头里改X-User-Id,就能读取任意用户信息。正确的方式是通过登录态解析当前用户,而不是信任前端传的用户标识。
3. 泄露高发源头追踪:sourcemap、环境变量与 Jenkins 部署日志
3.1 生产环境开着的 sourcemap:源码直接送到攻击者嘴边
我见过最多的泄露源头,就是生产环境居然还提供.js.map文件。sourcemap 的作用是让浏览器能把压缩代码映射回原始 TS/JS 源码,方便开发者调试。但只要线上存在index.js.map,任何人打开 DevTools 的 Sources 面板,都能直接看到完整的原始 TypeScript 源码、注释、甚至目录结构。
很多脚手架在开发模式默认开启 sourcemap,但构建生产包时没有关闭。Vite 项目里只要在vite.config.ts设置了:
build: { sourcemap: true }线上就会生成.js.map文件。有些版本在mode=production时默认不生成,可一旦有人为了排查问题手动开了 sourcemap 又忘关,风险就回来了。
为什么说这是“极低成本的泄露”?因为攻击者不需要做任何逆向,直接访问/assets/index-xxxx.js.map,拿到的就是接近源码级别的产物。如果一个项目里包含后端接口文档地址、数据库连接方式、内部注释,这些全都会跟着泄露。
3.2 构建期注入的环境变量:被打进产物里的“隐形字符串”
前端项目的环境变量,比如VITE_API_BASE_URL、VITE_CLOUD_KEY,在构建时会被打包进最终 JS 文件。这不是 bug,而是构建工具的既定行为。Vite 会把import.meta.env.VITE_XXX替换成对应的字符串字面量,Webpack 的DefinePlugin也是同理。
这意味着,你在.env.production里写的所有VITE_前缀变量,都会在产物里留下明文痕迹。哪怕是内网域名、测试环境地址、第三方服务的临时 key,只要被写成VITE_前缀,它就会进入 bundle。
我踩过的坑是:把上传文件的 OSS bucket 名称和地域信息放到了VITE_UPLOAD_BUCKET里,结果被搜索引擎收录了页面 JS,别人直接拼出存储桶地址,然后尝试列举文件。虽然没有造成实际损失,但这种事很容易在不知情时发生。
建议:运行时配置尽量不要通过构建期环境变量注入,而是通过后端接口动态下发。比如页面加载后调用/api/config,拿到的 JSON 里才包含接口前缀、功能开关等配置。这样一来,不同环境只需改后端配置,前端代码和构建产物完全一致,也不会在产物里暴露出环境差异导致的“部署和本地代码一样但返回内容不一样”问题。
3.3 Jenkins 部署过程中的日志暴露
另一个高频泄露源是 CI/CD 工具,尤其是 Jenkins。前端项目部署时,流水线里常常会执行:
npm install npm run build scp -r dist/* root@server:/usr/share/nginx/html/如果这些命令里包含环境变量、私有 registry 地址、NPM token,这些信息会直接出现在 Jenkins 的构建日志里。更严重的是,有些团队会把.npmrc文件直接放到项目根目录并提交到 Git 仓库,里面写着//registry.npmjs.org/:_authToken=${NPM_TOKEN},然后 Jenkins 的凭据绑定变量名又恰好能在日志里打印出来。
如果 Jenkins 服务本身没有做好访问控制,或者构建日志可以被普通员工甚至外网访问,攻击者就能通过日志拿到不少内部信息。更隐蔽的是,如果前端构建过程中下载了私有依赖包,这些包内的逻辑也可能被复制到 dist 产物里,变成泄露的一部分。
我做过的清理动作包括:
- 把
.npmrc里的 token 改为使用 Jenkins 凭据绑定,不再提交到仓库 - 流水线中禁止打印含
token、key、secret的环境变量 - 构建日志保留策略缩到 7 天
- Jenkins 本身接入 SSO 和权限控制,不再允许匿名访问
3.4 被忽略的第三方脚本与低代码平台
很多前端项目会引入分析统计、错误监控、客服系统等第三方 SDK。这些 SDK 加载的代码不在你控制范围内,但它们运行时可能会上报大量页面信息和用户数据。如果某个第三方脚本里存在安全漏洞,或它的上报域名被劫持,攻击者就能借机窃取用户输入。
低代码平台也要单独拿出来说。低代码开发越来越流行,很多业务后台是用低代码平台搭出来的,前端呈现为一份配置化的“协议代码”。业务逻辑都在配置文件里,比如“哪个按钮可见”“哪个操作需要什么角色”。这些配置最后会打包进前端产物,攻击者很容易看懂,然后尝试直接调用底层接口。低代码平台的权限能不能压住后端,完全取决于平台有没有生成真正的后端鉴权逻辑,而不是仅仅在前端隐藏入口。
4. Vue3 代码混淆的真实作用:提高成本,不等于建立边界
4.1 压缩和混淆不是一回事
先区分两个概念。
压缩(minify)是去掉空白、缩短变量名、压缩字符串,目的是减小体积。它不改变控制流,不改变逻辑结构,只是更难读一点点。用 terser、esbuild、Vite 默认就能做。
混淆(obfuscation)是主动改变代码结构,让数据和执行逻辑变得难以理解。常见的混淆手段包括字符串数组化、控制流扁平化、死代码注入、函数名动态生成、自执行函数拼装等。目的是增加逆向和阅读成本,但不提供真正的安全性。
Vue3 项目的混淆通常有两个入口:
- 使用构建插件,比如
vite-plugin-obfuscator,在 build 阶段对产物做混淆。 - 使用独立的
javascript-obfuscator包,在 CI 流水线里对 dist 再处理。
我在一个 Vue3 项目中用过vite-plugin-obfuscator,大致配置如下:
import obfuscatorPlugin from 'vite-plugin-obfuscator' export default defineConfig({ plugins: [ vue(), obfuscatorPlugin({ include: ['src/**/*.ts', 'src/**/*.vue'], exclude: [/node_modules/], apply: 'build', options: { compact: true, controlFlowFlattening: true, controlFlowFlatteningThreshold: 0.5, deadCodeInjection: true, deadCodeInjectionThreshold: 0.3, stringArray: true, stringArrayThreshold: 0.75, identifierNamesGenerator: 'hexadecimal', renameGlobals: true, } }) ] })加上混淆之后,产物可读性确实下降了很多,字符串也不直接出现在代码里了。但你要明白,它防的是“随手翻源码就能看明白”的人,防不了铁了心要逆向的人。因为浏览器最终还是要执行它,攻击者设置断点、观察变量、跟踪网络请求,照样能把核心逻辑逆出来。
4.2 混淆真正能防什么,不能防什么
具体来说:
- 能防:临时起意翻代码复制逻辑的普通人,或者搜索引擎直接全文搜索到关键字符串。
- 不能防:网络请求、token、响应数据、接口返回内容。只要请求发生在浏览器里,攻击者用代理抓包一定能看到。
Vue3 的代码混淆也一样,它能把axios请求的封装代码、API 路径的拼接过程变得难读,但攻击者只要发起一次请求,就能在 Network 面板看到实际请求的 URL、请求头、响应数据。所以混淆对“接口暴露”这个层面的安全没有任何防御作用。
你可能会问:那还做不做混淆?我的建议是:做,但要摆正心态。混淆的价值是提高攻击成本,减少一些低水平扫描和批量抓取,而不是作为安全边界。真正决定安全的是后端权限机制,不是前端代码能不能被看懂。
前端低代码开发里也一样,低代码平台生成出来的代码往往结构固定、命名规律一致,攻击者拿到一套就能举一反三。想靠混淆把低代码逻辑藏住,不太现实,必须在后端把权限控制做实。
5. 后端权限重构落地:把前端当成“不可信客户端”来设计
5.1 核心心法:后端默认拒绝,前端永远不可信
前端代码泄露带来的最大教训,不是“我们要把接口藏好”,而是“我们压根不该让接口的可见性承担安全职责”。重构防护体系,第一原则就是:把前端当成一个不可信客户端,任何来自前端的请求都要经过后端身份认证和权限校验。
这句话怎么落地?
- 所有涉及用户数据、写操作、敏感读操作的接口,必须要求有效登录态。
- 所有变动操作,必须校验当前用户是否拥有该操作的权限。
- 所有跨用户访问的数据,必须校验资源属主。
- 所有接口默认拒绝,只有显式声明公开的接口(如登录、注册)才放行。
- 不能因为接口“只在内部使用”就跳过鉴权。
一个典型的反面例子:
@app.get("/api/order/list") async def list_orders(request: Request): user_id = request.query_params.get("user_id") orders = db.query(Order).filter(Order.user_id == user_id) return orders这个接口信任前端传来user_id,攻击者遍历一下,就能看所有人的订单。改成正确姿势要从会话里取用户:
from fastapi import Depends from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials security = HTTPBearer() def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)): payload = verify_jwt(credentials.credentials) return get_user_by_id(payload["sub"]) @app.get("/api/order/list") async def list_orders(user: User = Depends(get_current_user)): orders = db.query(Order).filter(Order.user_id == user.id) return orders这样,用户从 JWT 里解析出来,而不是从前端参数里拿。即使攻击者知道了接口路径,没有合法 token 也是白搭。
5.2 认证与授权分离:登录只是起点
很多团队做到“登录后就能访问”就收手了,没继续细化“谁能访问哪个接口”。后端权限体系重构时,至少要把这两层分开:
- 认证(Authentication):你是谁?
- 授权(Authorization):你能做什么?
常见实现是 JWT + RBAC。用户登录成功拿到 JWT,JWT 里可以带user_id、role等基础信息;后端的每个路由通过依赖注入检查该用户角色是否匹配。
比如 FastAPI 里可以这样定义一个管理员依赖:
def require_admin(user: User = Depends(get_current_user)): if user.role != "admin": raise HTTPException(status_code=403, detail="Permission denied") return user @app.delete("/api/order/{order_id}") async def delete_order(order_id: int, user: User = Depends(require_admin)): # 只有管理员才能删除订单 ...如果项目复杂度更高,最好做成集中式的权限表或策略模块,而不是在接口函数里散落if user.role == ...。
5.3 第三方免登录场景:比如飞书网页应用该怎么接
现在很多企业内部应用会嵌入飞书等办公平台,用户在飞书里点开应用,可以“免登录”进入。从用户视角看是免登录,但技术实现上不是没有鉴权,而是鉴权交给了飞书身份体系。
常见的飞书网页应用交互流程:
- 前端在飞书容器内拿到一个临时授权码
code。 - 前端把
code传给自己的后端。 - 后端拿着
code+App ID+App Secret去飞书服务器换access_token或用户身份。 - 换取成功后,后端再签发自家的登录态(JWT / Session),返回给前端。
这里最关键的是:换 token 的过程必须在后端完成,App Secret 绝不能出现在前端 Vue 代码里。如果为了图省事,把App Secret写死在 Vue 项目里,那任何人从代码里把它抠出来都能冒充你的应用去换取用户信息,后续权限体系就成了摆设。
另外,有些“免登录”场景是前端直接带着飞书user_id来访问业务接口。这更危险。攻击者只需要把请求里的user_id改成别人的,就能越权操作。一定要让后端通过可信通道去飞书验证用户身份,然后签发自己的会话,而不是信任前端传的身份标识。
5.4 密钥与敏感操作:能不放前端就不放,放了你就要有应急预案
前端确实有些场景需要用到加密处理,比如登录密码加密传输、支付签名等。但请记住:所有能在前端执行的密钥逻辑,攻击者都一定能在浏览器里还原。如果你的流程里必须要用某个密钥做签名,那这个流程的反重放、时效性、配额限制必须控制得很严。
比如前端使用 RSA 公钥加密登录密码,公钥本身就是公开信息,风险不大。但如果前端用了某个对称密钥做接口加密,那这个对称密钥一旦泄露,整个加密体系就算破了。更稳妥的做法是改用 HTTPS + 后端验签,而不是在前端做大量加密。
密钥管理的几个原则:
- 前后端分离项目,前端不要保存任何长期有效的敏感密钥。
- 第三方云服务密钥全部迁移到后端或专用密钥管理服务。
- 需要临时凭证时,由后端签发短期、最小权限的临时凭证。
- 密钥泄露要有可追溯、可吊销的机制,不能一个 key 走天下。
6. 前端安全工程规范与可执行的检查清单
6.1 前端代码工程规范:从源头杜绝低水平泄露
安全不能只靠运维排查,要靠工程规范压实到日常代码里。我在团队里推过一份前端代码工程规范,核心几条是:
- 禁止在生产构建中开启 sourcemap。
- 禁止把任何密钥写入
VITE_开头的环境变量。 - 禁止在前端注释中写内网 IP、内部域名、后端账号密码。
- 禁止在 Git 提交中保存
.env、.env.local、.npmrc、serviceAccountKey.json。 - 提交代码前使用
.gitignore忽略本地配置和构建产物。
其中最容易被忽视的是注释。很多人在接口调用处顺手写一句“这里调的是管理后台的 xxx 接口”,或者注释一行“线上地址 http://10.0.x.x:8080”。只要这个文件被打进 bundle,这段注释就可能被别人用 grep 找出来。
6.2 构建与部署阶段的安全检查
前端项目在构建和部署阶段,可以做不少自动化检查,我会在 Jenkins 流水线里加几个步骤:
源码泄露扫描:构建完成后,对 dist 里的 JS 文件执行一次正则扫描,关键词比如
api_key、secret、BEGIN RSA PRIVATE KEY、password、内网 IP 段。一旦命中,直接中断流水线并报警。Sourcemap 检查:扫描产物里是否存在
.map文件,存在即失败。产物体积对比:结合容器镜像或 Nginx 目录,查看是否意外出现了 dev server、源码目录或 node_modules。
Jenkins 前端代码部署的时候,还有一个非常容易踩的坑:把整个项目目录同步到 Nginx 的 html 目录。比如有人直接执行:
rsync -avz --delete ./ root@server:/usr/share/nginx/html/结果把.git、node_modules、src、Jenkinsfile全部同步上去了。攻击者访问https://站点/.git/HEAD就能直接 dump 整个源码仓库。正确做法是只同步dist目录,或者先用 tar 打包 dist 再解压到站点目录。
6.3 “部署和本地代码一样,但前端返回内容不一样”的系统排查法
再回到这个让人头大的问题。如果以后你遇到类似情况,按这个顺序排查:
- 先确认构建产物和本地是否真的是同一份代码:比较
package-lock.json的 lockfile 版本、构建时间、产物文件名哈希。 - 再确认环境变量差异:对比
.env.development、.env.production、Jenkins 里配置的构建参数。 - 然后对比后端环境:测试环境和本地环境是否走同一套鉴权、是否关闭了本地鉴权、是否有 IP 白名单。
- 最后检查中间层:Nginx 有没有重写规则、CDN 有没有缓存旧资源、网关有没有转发差异。
从安全角度,这类问题的本质往往不是“代码不同”,而是“不同环境下后端权限策略不同”。所以排查完之后,还应该顺便确认一下:后端的这些权限策略差异,是否被前端代码泄露所利用。如果测试环境某接口不鉴权,攻击者找到路径后就能在测试环境里做越权测试,进而摸清业务数据规律。
6.4 一份可执行的安全扫描清单
我给自己项目列了个“前端安全自查清单”,每次发布前都会跑一遍,分享出来供参考:
| 检查项 | 检查方法 | 结果 |
|---|---|---|
| 生产环境是否开启 sourcemap | 搜索 dist 内.map文件,无则通过 | 通过 |
| 产物中是否存在密钥 | grep 搜索api_key、secret、token | 通过 |
| 产物中是否存在内网 IP | grep 搜索10.、192.168.、172.开头的 IP | 通过 |
| 敏感接口是否都有鉴权 | 对照接口文档,逐个确认 | 待补 |
| 低代码平台权限是否落到后端 | 抽查关键操作的后端日志,看是否有权限校验 | 待补 |
| Nginx 目录是否只包含 dist | curl 访问站点根目录,确认无法列举目录 | 通过 |
| Jenkins 日志是否泄露变量 | 随机翻最近构建日志,看是否出现token= | 通过 |
Git 仓库是否泄露.env | 检查 .gitignore 与历史提交 | 待补 |
| 第三方密钥是否由后端托管 | 前端代码搜索AccessKey、AppSecret | 通过 |
6.5 一个随时能开始的“侦探练习”
如果你暂时没有精力做大改造,我建议你先做一个极低成本却极其见效的“侦探练习”。打开部署好的前端站点,按下 F12,切到 Sources 面板,点击任意主 JS 文件,按Ctrl+Shift+F全局搜索,关键词先搜这几个:
http://或https:///api/adminsecretpasswordtoken
你会很惊讶地发现,一个普通项目能搜出来多少敏感信息。我上次帮朋友排查一个后端接口权限问题,就是这么搜的:搜索结果里直接跳出一个未鉴权的后台接口,数据表名、字段名、甚至联调环境数据库地址都写着注释。后面的事就不用多说了。
这个练习的意义在于:它让你直观看到“前端代码泄露”这四个字在真实项目里到底是什么样。不是抽象的安全概念,而是能直接点击就能复制的一行行接口地址和注释。看清楚之后,你自然会想把后端权限体系重构起来,而不是继续在“藏代码”这件事上做无用功。
防护体系重构从来不是买一个 WAF、上一个混淆插件就能完事。真正的关键动作是:后端的每个接口都把自己当成被全世界围观来设计,前端的所有代码都当成会被任何人阅读来编写。把这句心态转过来,才是这套体系最扎实的起点。