首页 / 资讯中心 / 文章详情

前端安全实战指南:从XSS到供应链攻击的全面防护

前端安全实战指南:从XSS到供应链攻击的全面防护 ★ FEATURED ARTICLE
1. 前端安全的攻击面到底有多大1.1 前端为什么总被当成“最不重要”的一环干前端这么多年我见过太多团队把安全当成后端的事情。开会聊安全提的最多是“接口鉴权”“数据库加密”“服务器防护”很少有人认真看一眼自己打包出来的那个 JS bundle 里藏着什么。但实际情况非常打脸。我在某公司做技术复盘时统计过近两年能查到的安全事件里有接近四成是从前端发起的或者说前端是攻击链上最关键的一环。攻击者不需要攻破服务器只需要在你网站的脚本里找到一个可注入的缝隙或者在依赖树里埋一个恶意包就能在用户浏览器里干很多事情偷 cookie、劫持表单数据、伪造页面内容。前端不安全不是“用户体验差点”而是整个系统被撕开一个口子。现代前端已经承担了大量业务逻辑SPA里有路由、状态管理、本地缓存、第三方登录、支付回调这些全是攻击面。前端如果只是“把接口返回的数据渲染出来”的展示层那确实不重要可今天的前端早就不是展示层了它是一个完整的客户端应用。我自己的习惯是拿到任何项目先不看交互设计、不看组件拆分先找接入点哪里渲染了用户输入、哪里写死了密钥、哪里用了innerHTML、哪里直接拼接 URL 参数到页面里。这些位置就是前端安全的主战场。1.2 浏览器这个“运行环境”天然有攻击面前端的代码不是运行在你自己的服务器上而是下载到用户浏览器里执行的。这意味着三件事第一代码完全裸奔。任何用户都可以打开开发者工具看你加载了哪些脚本、发了哪些请求、带了哪些参数。混淆和压缩只能提高阅读门槛没办法真正藏住秘密。第二运行环境是用户自己的设备面对的是不可信的输入。地址栏的 URL 可以被拼出各种奇怪的值localStorage 里的内容用户自己能改请求可以被代理工具拦截并篡改。你维护的前端应用随时要处理被攻击者精心构造的输入。第三第三方资源不可控。你的页面里可能引用了字体库、CDN 上的公共脚本、统计脚本、广告脚本任何一个环节被劫持整个页面的信任链就断了。以前有过公共 CDN 上的某个 JS 库被人上传恶意版本的事件很多站点的用户就被重定向到了钓鱼页面这事就是典型的前端供应链事故。这三件事合起来就是一句话前端安全就是在“代码不设防”和“环境不可信”的前提下尽可能地把可控范围内的风险管住。很多开发者的误区是不知道哪些地方属于可控范围也不知道哪些地方已经失控了。1.3 盘点现代前端项目的攻击面清单我整理过一份攻击面清单每次接手新项目都会对照着过一遍。这份清单不复杂但是覆盖了绝大多数前端安全问题的入口DOM 渲染入口innerHTML、document.write、outerHTML、insertAdjacentHTML这类直接解析 HTML 的 API 是 XSS 的高发区。URL 参数处理从location.search或location.hash中取值并用于渲染、跳转、埋点容易产生反射型 XSS 和协议注入。表单与用户输入搜索框、评论框、富文本编辑器、上传文件名任何用户可控的字符串进入 DOM 或后端存储后再回显都是风险点。本地存储localStorage、sessionStorage、IndexedDB 里保存的 token、用户信息、业务缓存一旦被 XSS 拿到等于把身份凭证交给了别人。第三方依赖npm 包和 CDN 资源是当前前端最大的隐性风险源后面我会专门展开。接口调用与鉴权前端代码里写死的 AppKey、Secret、内部接口地址以及 token 的存放方式。跳转与打开外部链接window.open、location.href的跳转目标是否被用户输入控制target_blank是否缺失noopener。iframe 嵌入与交互自己的页面能不能被别人 iframe 嵌入刷卡劫持和点击劫持都跟这个有关。CSP 配置是否允许了不受控的脚本来源是否允许unsafe-inline和unsafe-eval。错误信息与调试日志生产环境是否泄露了堆栈、接口路径、内部字段名。每次做代码评审我会重点看这几个地方花的时间不多但往往能抓到几个值得修的问题。前端安全不是什么高深的技术很多时候就是把这些入口一个个管住别留裸奔的口子。2. XSS前端安全问题里的头号常客2.1 XSS 仍然是最容易踩中的坑跨站脚本攻击也就是 XSS放在第一优先级讲不是因为它新而是因为这些年我见过太多项目在同一个地方反复栽跟头。XSS 的触发前提非常简单用户的输入被当作代码执行了。最经典的场景是搜索框。用户输入一段内容页面拿到这段内容之后把它拼接到 HTML 里显示“您搜索的关键词是xxx”。如果你直接拼接而不做任何处理攻击者输入img srcx onerroralert(document.cookie)这段内容就会被浏览器当成 HTML 标签解析onerror事件被执行恶意脚本就跑起来了。有人可能会说“现在的框架已经帮我转义了”。确实主流框架在绝大多数情况下会自动转义数据绑定里的字符串。但问题出在那些“跳出框架保护”的 API 上v-htmlVue、dangerouslySetInnerHTMLReact、innerHTML、insertAdjacentHTML。很多开发者为了省事在需要渲染富文本内容时直接用这些接口又把服务端返回的 HTML 原样塞进去一旦这段 HTML 里混入了script或者事件属性整个页面就沦陷了。XSS 的变体也不止一种。反射型是临时拼到 URL 上的存储型的把恶意内容存到服务端所有用户访问都会中招DOM 型的是纯前端问题恶意数据从不经过服务端直接在前端代码里流转最终被 DOM API 消费这种最难排查因为日志里根本看不到异常接口请求。我一个同事之前处理过一个问题用户提交了一个包含style标签的评论内容通过富文本编辑器保存后其他用户打开详情页时页面样式被改了整个布局错乱。当时还没触发到脚本执行但如果攻击者换成img onerror或者引用外部脚本事情就没这么简单了。2.2 防御 XSS 不能只靠“转义一下”防御 XSS 的核心原则是永远不要信任用户的输入永远不要信任任何即将进入 DOM 的字符串。这句话执行起来分几个层面。首先是输出编码。把用户输入渲染到 HTML 标签内、属性内、JavaScript 字符串内、CSS 内每种上下文需要的编码方式都不一样。框架大部分时候帮你做了这一点但你要知道它帮你做了什么这样遇到v-html这类接口时才不会放松警惕。其次是输入校验。对输入内容做白名单限制比如用户名只允许字母数字下划线URL 只允许 http/https 协议并且校验域名。这个属于第一道防线不能作为唯一防线因为校验规则总有想不到的特殊情况比如以前很出名的javascript:协议注入就是经过 URL 解析器的层层兼容最终在一个属性里被执行。再就是 CSP内容安全策略。我认为这是前端安全里被重视程度严重不足的防护手段。CSP 用 HTTP 响应头告诉浏览器你这个页面只允许加载哪些来源的脚本、样式、图片不允许执行内联脚本和eval。即便 XSS 注入了script如果这个脚本不在白名单内浏览器会直接拦住不执行。这相当于给 XSS 攻击加了一层最后的保险。CSP 的配置需要花心思因为太严格会误伤业务太宽松又形同虚设。我见过比较合理的起步配置是这样的Content-Security-Policy: default-src self; script-src self https://static.example.com; style-src self unsafe-inline; img-src self data:; object-src none; base-uri self; frame-ancestors none这里面script-src只允许同源和指定静态域名禁掉了unsafe-inline线上的服务端渲染注入脚本会被直接挡住。object-src none禁掉 Flash 之类的老古董。base-uri self防止基础标签被篡改后导致后续所有资源相对路径被劫持。这几项对大部分业务来说都是可以接受的严格程度但收效非常明显。第三层才是消毒清洗。对于确实需要渲染富文本的场景不能用“转义所有 HTML”的办法那样会把格式全毁掉。这时候要用专门的 HTML 白名单清洗工具比如 DOMPurify它会把字符串解析成 DOM然后按照允许的标签和属性列表过滤掉危险内容。我实际项目中处理富文本的代码大致长这样import DOMPurify from dompurify; const cleanHtml DOMPurify.sanitize(rawHtml, { ALLOWED_TAGS: [p, br, strong, em, ul, ol, li, a, img, blockquote, code, pre], ALLOWED_ATTR: [href, src, alt, title, target], ALLOWED_URI_REGEXP: /^(?:(?:https?|mailto):|[^a-z]|[a-z.\-](?:[^a-z.\-:]|$))/i });加ALLOWED_URI_REGEXP是为了拦掉javascript:这样的危险协议H5 的data:text/html也在列表里被过滤掉。这套配置在真实项目里跑了很久评论和文章内容没有再出过问题。2.3 实战排查技巧怎么快速定位 XSS 风险点接手老项目或者做代码评审时我有一套很固定的排查流程。先全局搜危险 APIinnerHTML、outerHTML、insertAdjacentHTML、document.write、v-html、dangerouslySetInnerHTML。把这些出现的地方全部列出来逐个看数据是从哪里来的。如果数据源是自己写死的常量字符串问题不大如果数据源是接口返回、URL 参数、本地存储就要重点审。然后看数据流转路径。前端的数据流转经常跨文件A 页面收到接口数据存入 storeB 组件从 store 取出来渲染。你只看渲染处可能觉得“数据是内部流转的应该安全”但中间可能经过某个工具函数做了字符串拼接又把用户可控的字段混进去了。最后是动态测试。不用搭复杂的攻击环境直接在浏览器控制台里选几个高风险输入点输入一段无害的测试 payloadimg srcx onerrorconsole.log(window.location.href)如果控制台打出了当前 URL说明 XSS 已经触发了只是比较隐蔽。有一个我在实际项目里踩过的坑想特别提一下前端框架的模板语法在某些场景下会被绕过。Vue 的模板插值默认会转义但如果你把模板写在 HTML 文件里的script typetext/x-template或者template标签内然后又被v-html渲染那么模板里的内容等于被二次解释转义保护就失效了。这类问题静态搜索不一定能找到需要靠测试触发。3. CSRF、点击劫持和其他容易被忽略的“老毛病”3.1 CSRF 的现状依然危险但没那么容易利用了CSRF 的历史很长攻击原理也简单用户在 A 网站登录了浏览器里带着 A 网站的 cookie攻击者在 B 网站构造了一个请求诱导用户点击浏览器带着 A 网站的 cookie 去请求 A 网站的某个修改接口服务端看到 cookie 有效就认为请求可信执行了操作。以前的服务端接口很多是纯 cookie 认证的CSRF 成功率非常高。现在情况好一些因为主流做法是前后端分离前端用 JWT 或自定义 token 做认证token 放在Authorization头里而跨站请求受同源策略限制攻击者没办法读取也不方便构造自定义头。但依然不能掉以轻心因为还有几种情况会绕过某些系统保留了 cookie 会话机制比如内网系统、旧版接口。业务接口为了兼容做成了application/x-www-form-urlencoded的 POST 提交这类请求可以用隐藏表单自动提交来伪造。部分接口用了 GET 来做状态变更操作比如“通过 URL 执行操作”这种是最容易被利用的。CSRF 的防御手段不复杂核心是验证请求来源和携带随机令牌。验证来源可以用校验Origin和Referer头来实现但 Referer 有时候会丢失不能作为唯一依赖。随机令牌方案比较成熟服务端给每个会话生成一个不可预测的 CSRF Token前端在发请求时带上服务端校验不匹配就拒绝。现在主流框架都有现成的中间件不用自己从头造轮子。我自己的习惯是新项目一律使用自定义请求头方式来发 POST/PUT/DELETE 请求比如X-Requested-With: XMLHttpRequest服务端校验这个头的存在。跨域请求带自定义头会触发预检请求攻击者构造的普通表单提交根本没有办法带上这个头。这个方法算是轻量又有效。3.2 点击劫持安全测试里最容易被忽视的一个点点击劫持典型的攻击方式是攻击者把目标网站嵌入到自己页面的 iframe 里再用 CSS 把它透明化叠在一个“看起来正常的按钮”下面。用户以为自己点了“领取奖励”实际点到了目标网站的“删除账号”按钮。这个问题的危害被低估了因为利用场景需要精细的页面布局配合但对一些操作步骤明确的站点钓鱼成功率不低。防御手段主要分两层。第一层是 HTTP 响应头X-Frame-Options: DENY或SAMEORIGIN告诉浏览器这个页面不允许被嵌入到其他站点。第二层是 CSP 里的frame-ancestors指令它比X-Frame-Options表达力更强可以精确控制哪些域名可以嵌入你的页面。对大部分不需要被第三方嵌入的应用我的建议是直接禁掉X-Frame-Options: DENY Content-Security-Policy: frame-ancestors none如果业务上确实需要被嵌入到特定合作方页面frame-ancestors可以写合作方的域名白名单比全放开安全得多。还有一个容易遗漏的点是页面上的敏感按钮需要二次确认这是业务层面的兜底。即使点击劫持防御因为某些兼容原因失效用户对“确认删除”“确认转账”的弹窗有意识也会多一层保护。我在某系统里见过状态变更类接口没有确认弹窗直接单次点击生效就属于设计缺陷。3.3 被忽视的 iframe、open 和跳转细节顺着点击劫持往下说iframe 还有一个特性父页面可以通过脚本访问同域 iframe 的内容而跨域的页面在某些条件下也会被部分访问。前端的安全边界往往在 iframe 与父页面之间被模糊了。更常见的一个问题是target_blank不带relnoopener noreferrer。这在老代码里非常常见。当你用target_blank打开一个新页面时新页面可以通过window.opener访问你的页面对象如果新页面被攻击者控制它可以篡改你页面的 DOM或者把用户重定向到钓鱼页面。现在的浏览器大部分默认带了noopener行为但代码层面显式声明依然是安全的习惯兼容老浏览器也稳妥。跳转类的安全问题集中在location.href、window.open的地址来源。有些业务需要支持“点击通知跳转到详情页”跳转 URL 可能是服务端下发或 URL 参数携带的。如果这个 URL 没有经过白名单校验攻击者可以构造javascript:alert(document.cookie)或者data:text/html,script...之类的协议诱导用户点击一样能让恶意脚本在页面上下文中执行。我的做法是封装一个统一跳转函数任何 URL 经过它就强制校验协议和域名白名单const ALLOWED_DOMAINS [www.example.com, passport.example.com]; function safeRedirect(target) { let url; try { url new URL(target, window.location.origin); } catch (e) { return; } if (url.protocol ! https: url.protocol ! http:) { return; } if (!ALLOWED_DOMAINS.includes(url.hostname)) { return; } window.location.href url.toString(); }这个函数虽然简单但它把“跳转到哪”这个问题从“业务代码里到处贴”变成了“统一受控”后续要加域名白名单也只需要改一处。4. 供应链攻击与依赖安全最大的隐性风险4.1 你项目里的一半代码都不归你管谈到前端安全问题我最想强调的就是依赖安全。一个中型前端项目node_modules里几千个包很正常这些包基本都不是你写的有些是明面上的依赖有些是依赖的依赖。你根本不知道哪个包在哪次小版本更新里动了手脚。供应链攻击的思路也很简单攻击者不直接攻击你的网站而是入侵一个有很多人都在用的开源包发布一个带恶意代码的版本只要有人升级依赖恶意代码就被装进他的构建产物里。恶意的代码可以做很多很隐蔽的事情在构建时读取环境变量里的密钥往页面的 HTML 里注入一段脚本收集用户的输入数据并通过网络发出去。我在某次排查里就遇到过一次典型事件某个插件包在某个版本里增加了一段代码检查navigator.userAgent如果是特定浏览器就跳转广告地址。因为那个包被业务直接引用所有相关页面在那个时间段都出现过异常跳转。当时团队里没人往供应链攻击上想查了好久才发现是依赖的锅。4.2 防住供应链攻击的操作清单防供应链攻击没有一招制敌的办法全靠好几个层面的习惯叠加。第一锁定版本。package.json里用精确版本号而不是^或~这样意外升到被污染的次版本的概率会小很多。同时启用 lock 文件package-lock.json或yarn.lock并且把它提交进版本库。lock 文件锁定的是一整棵依赖树的具体版本光改package.json不一定能保证实际安装的版本一样。第二构建环境里设置最小权限。不要把私有仓库的凭证、生产环境的密钥写进 CI 配置或者 Docker 镜像里更不要在前端代码仓库里保存任何真实密钥。那些需要在前端用到的标识性参数要区分可公开的和真正敏感的能放到服务端校验的就不要塞进前端包。第三做依赖审计。npm audit可以检查已知漏洞库npm outdated可以看版本落后情况。这个不完美因为审计跑的是本地已知漏洞库新型漏洞不一定能覆盖到但作为常规体检还是很有价值。第四用私有源和完整性校验。企业内部搭建私有 npm 源先把包拉进去做检测再让项目从私有源安装。同时可以开启npm install的完整性校验确保下载下来的包内容和发布时一致。这一步治不了“包本身就是恶意”的问题但能防住“下载源被劫持”的问题。第五也是我觉得很多团队最该做的定期审查直接依赖里那些你可能没注意过的包尤其是那些仅构建期使用、但权重很高的小工具。大牌框架被人盯着反而相对安全真正危险的是那些几万周下载量、作者就一个人的工具包。它可能是很长一段时间都没更新的老包也可能是个刚出现的小众新包。看到这类包我会特意去看它的 README、GitHub 仓库活跃度、最近几条 release 的变更内容。花不了几分钟但能避免很多意想不到的坑。4.3 给构建产物做一次“减重”和“安检”依赖安全除了管来源还有一个实际操作审查最终构建产物里到底包含了哪些代码。有一次我在一个新项目里看到控制台报了一个陌生库的错误查了半天发现是某个工具函数库里带了一个完整的日期处理模块而这个工具函数库是被另一个包间接依赖的。也就是说一行简单的代码拖进来一个几万行代码的库。这不一定是安全问题但它确实扩大了攻击面——每一行你无法掌控的代码都是潜在的不确定因素。所以我建议定期做一次构建产物体积分析和代码内容抽查。用 sourcemap 分析工具看一下线上加载的 JS 文件里每个第三方模块占了多少体积抽查几个模块快速确认它出现在产物里是不是符合预期的。如果一个老项目里躺着一个来源不明的包尽早排查处置别等出了事再去翻依赖树。另外前端代码打包后尽量不要直接用公共 CDN 加载关键的第三方脚本尤其是那些需要控制 DOM 的库。公共 CDN 一旦被劫持或出问题你的页面会被强插不可控脚本。把脚本下载到自己的静态资源域名上配合子资源完整性SRI校验用script integrity...告诉浏览器“这个脚本必须是这个哈希值否则不加载”能最大程度避免公共依赖被替换的风险。5. 敏感信息泄露藏在代码里的“裸奔秘密”5.1 前端代码里那些你以为藏得住的东西所有公司都遇到过这类问题某个前端项目里写着一个地图服务的 Key后来这个 Key 被爬虫扛走了对方拿它刷了几十万次接口账算在你头上。为什么会出现这种事原因是前端代码本质上要交付给用户里面所有的常量都是透明的。你写“这段代码放在 minified 的 bundle 里别人应该找不到”但实际 minified 只是把变量名缩短和压缩字符串常量依然是明文的。别人用格式化工具展开密钥一目了然。需要重点自查的敏感信息包括服务端 API 的完整内网地址这类地址暴露以后会直接成为攻击者的扫描目标。各种第三方服务的 AppKey、Secret、Token尤其是在客户端 SDK 初始化时用到的。数据库、缓存、消息队列的地址和账号密码。这个属于低级失误但真实项目里见过不止一次。内部系统的字段名、接口结构、错误码含义这些看似不敏感但能为攻击者节省大量探测时间。泄露在日志上报里的堆栈信息包含源码路径和变量名。处理这些问题的原则只有一个能不能不在前端出现的就坚决不要出现。环境变量也不是万能的因为构建时的环境变量最终会注入到产物里不是真正的“隐藏”只是换了个名字而已。5.2 错误信息和调试日志是最容易被忽略的口子很多前端项目上线前会清理console.log但错误监控 SDK 和全局错误处理不一定清理干净。有一次我在一个生产环境页面上看到报错堆栈直接弹到页面右下角里面带着一个完整的内网 API 路径一看就是全局错误处理没有做生产环境的降级。我一般采用的做法是区分环境配置const isProduction import.meta.env.PROD; window.addEventListener(error, (event) { if (isProduction) { // 只上报关键信息不展示堆栈详情 reportError({ message: event.message.slice(0, 200), fileName: event.filename, // 可能为空也需要脱敏 lineno: event.lineno }); } else { // 开发环境保留完整堆栈方便调试 console.error(event.error); } });还有一个小细节不要在错误上报的 payload 里带上完整的 location.href。因为 URL 里经常带着用户的敏感参数如 token、手机号、搜索关键词。上报之前先做一下 URL 脱敏把 query 参数里的可疑字段替换掉。5.3 前端也能做“指纹与风控”说点稍微进阶的前端安全不只有“防别人攻击你”还有“识别谁在攻击你”。很多风控体系会依赖前端采集的设备指纹canvas 指纹、WebGL 参数、浏览器语言、时区、屏幕分辨率、UA 等组合成一个特征字符串配合 IP、账号行为数据来判断当前请求是不是自动化工具或攻击脚本。这里涉及一些敏感取舍的做法但其中有一个值得推荐的思路前端指纹不是用来识别单个用户身份的那涉及隐私问题而是用来标记异常模式和批量攻击的。比如某账号在短时间内用几十个不同的指纹登录就有明显的脚本化特征某个 IP 上的指纹大量重复也说明可能是代理池在跑自动化。这类能力不是小团队标配但明确一个原则对任何人都适用前端采集的任何数据都不可信只能作为服务端风控的一个参考维度不能作为唯一判据。攻击者完全可以伪造指纹、模拟真实浏览器行为服务端还是要以自己的数据和规则为准。我处理过的一个案例里某系统的验证码滑块突然被大量绕过排查到最后发现是前端把滑动轨迹上报给了服务端服务端只校验了“轨迹是否平滑”这一个维度攻击者用脚本模拟了一段物理轨迹就绕过了。之后的修复就是服务端引入多维度行为分析不再单独信任前端上报的数据。6. 让安全变成习惯一套可落地的日常防线6.1 给团队一份可抄作业的安全清单前面聊了很多具体问题落到日常开发里最有价值的是一份能直接照着做的安全检查清单。我把自己团队里用的那份整理出来它不是什么合规体系就是管住最关键的几十项代码全局搜索危险 API出现一个处理一个不允许搁置。所有用户输入渲染处确认走的是框架默认转义流程不走v-html和dangerouslySetInnerHTML。富文本内容必须过白名单清洗器。外部 URL 跳转必须过域名白名单校验。所有第三方依赖版本锁定lock 文件进仓库。新增依赖前快速看一眼包的来源和活跃度迹象。构建产物里禁止出现任何形式的密钥和内部地址关键字。生产环境关闭调试日志错误上报脱敏。服务端接口按需配置 CSRF 校验和来源校验。全站配置 CSP 响应头至少包含default-src self和frame-ancestors none。上传文件的域名和主站域名隔离防止上传的 HTML/SVG 文件在用户浏览器里执行。静态资源上传时做 Content-Type 校验和白名单禁止上传 HTML 文件后以 HTML 类型被直接访问。我之前在某个项目里就是按这条清单过了一遍查出了两个高危点和一个中危点。高危点里有一个是富文本内容没有清洗直接渲染的另一个是内网地址被写在构建常量文件里。都是老业务沉积下来的问题发现之后修复并不难难的是没人愿意系统地查一遍。6.2 推荐常用的安全检测工具和检查思路工具方面我按“静态扫描、运行时检测、依赖审计、人工核查”四类来配静态扫描用 ESLint 的安全插件它能帮助发现一些明显的问题模式比如innerHTML的使用配合项目规范可以设为 warning 或 error、不安全的target_blank等。运行时检测可以用浏览器插件直接模拟常见攻击 payload也可以给测试团队提供一份“前端安全冒烟用例”每次发版都跑一遍。依赖审计用建设平台提供的开源漏洞扫描服务不完美但值得纳入日常流程。人工核查永远是最重要的一环工具只能发现问题模式真正的业务风险要靠人看代码逻辑来判断。我的经验是新项目每次代码评审都要把安全检查清单过一遍老项目每季度安排一次安全专项 review每次全量开会把查出来的问题记录在案规定修复时限。6.3 从被攻击到恢复的应急思路前端安全做不到 100% 防御所以应急响应也要提前想一下。不用搞复杂至少想清楚几件事。第一如果发现线上页面被注入了恶意脚本第一时间要知道如何快速下线风险。最有效的手段是从服务端配置里临时封锁相关路径或者上静态页面挡在前面同时把能定位的注入源是哪个接口出问题、哪个依赖被污染找出来。第二被污染的前端资源要全部重新构建发布。如果问题出在某个 npm 包要退回相邻的安全版本并检查该包在这段时间发布的上游依赖链是否还有问题。第三审计影响面。这一步往往被忽视但它才是决定事件严重等级的关键。恶意脚本运行了多久采集了哪些数据这些数据传到了哪里有没有迹象表明某个特定用户的 cookie 已经被盗、账号已经被操作这些问题的答案直接影响后续的通报范围和整改动作。我在处理这类事件时体会最深的一步就一个别慌。前端出安全事件虽然吓人但大多数情况下影响都可以控制前提是你知道哪些日志还在、哪些链路还能追溯。所以平时构建发布留好时间戳和版本信息前端监控平台里把错误日志和接口日志留够回溯时间这些“看起来不紧急”的基础设施真到出事的时候就是命根子。6.4 安全不能只靠“一波整改”项目常见的情况是领导发话搞一波安全整改团队突击查几天把明显的问题修了然后——就没有然后了。这样的整改过三个月再看一半的问题会重新冒出来。原因很现实业务在快速迭代新代码在不断增加老问题修了新问题很快又会出现。前端安全不是一次性的需求它是开发流程里的一个常驻环节需要靠习惯而不是意志力来维持。我个人比较推崇的落地方式是把它融入已有的研发流程里。提测清单里加上安全自查项代码评审模板里加上安全关注点发布检查里加一条“生产环境无调试日志和敏感信息关键字”。这些都不需要额外开评审会不增加多少时间成本但长期坚持下来项目整体安全水位会明显高于那些不做这些事的团队。6.5 再讲两个身边的真实体会最后说两个我从实际项目中带出来的体会算是给这篇长文收个尾。第一个体会前端安全问题往往不是“技术多高深”而是“流程上没人管”。我跟进过的几个出问题的项目事后复盘几乎都能找到共同点——代码评审环节没人看安全点依赖升级流程没人把关上线前也没有安全检测步骤。技术方案再对流程不兜底早晚还是会漏。第二个体会要平衡好安全和业务迭代的关系。我不是“为了安全什么都不让上线”的守门员类型太激进的安全策略在真实业务里根本没有生存空间。合理的方式是分级对待高风险的范围比如涉及用户支付、敏感数据展示、第三方登录的区域安全要求做到最严普通业务功能满足基本防护即可别为了一个展示页面引入复杂的安全流程把团队拖垮。在我个人的实际体验里真正能给前端带来安全感的其实不是某个工具也不是某个框架特性而是两个习惯碰到用户可控的数据先想想它会不会变成代码执行碰到没来由的第三方依赖先翻翻它的来源。把这两个习惯刻进日常开发节奏里比任何“安全月”整改都管用。
阅读完成 · 觉得有帮助?
咨询建站