【第2118期】前端安全生产在ICBU的探索与落地
前言
前端安全生产,第二次见到这个词了。今日早读文章由阿里@陈波分享。
@陈波,花名定亦,来自阿里巴巴ICBU技术部互动&端技术团队,目前负责前端安全生产相关工作,集团前端攻防演练核心成员。
正文从这开始~~
随着前端专业技术的深度发展、精细化分工,前端开发在整个研发链路上扮演着越来越重要的角色,前端安全生产也成为了大家关注的重点。如何使前端工程师从研发、发布、上线整个链路更加有保证,本次我将与大家分享ICBU在安全生产预防、发现等领域的探索与落地。
0.目录
1.什么是前端安全生产?
从早期的web1.0、jquery时代,大家追求的是短平快的完成页面,前端同学做的更多的是写html和css,顺带一些简单的js逻辑。
随着互联网的发展,我们更多的追求用户体验,对前端的要求也越来越高,前端工程变得复杂,不可维护。这个时候,有了单页到spa技术的转变、seajs、MV*框架等等,有效的帮助我们解决了前端开发中复杂度的问题。
随着互联网的成熟,网络、终端带来的红利,Nodejs的推广给前端带来服务端的一些能力,前端进入一个“大前端时代”。这个时候,前端同学在跨端、互动直播、到serverless等都有了发力的点。前端的工程化开始变得完整,也可以完整的参与到整个软件研发的全流程,前端同学面临的既有服务端的可用性,也有前端独特的消费场景。
复杂度越高,一定程度上,我们遇到的挑战也会越多,前端可能造成的问题也会越多。所以前端安全生产,其实就是围绕前端研发全链路高质量交付这个问题,通过一系列自动化流程机制,保障线上业务稳定运行
2.为什么要做前端安全生产?
我们分析了过去2年整个ICBU全部的故障,从三个维度去提炼了故障的一些指标
故障按类别
故障发现渠道
故障发现&恢复时长
通过以上数据分析,可以得到如下结论:
前端可以产生故障,甚至是影响面极大的重大故障,比如:搜索成功率下跌、订单创建成功率下降等等
变更执行产生的故障大约占全部故障的60%以上。所有故障中约70%左右的问题,我们可以通过ui自动化回归来避免
故障的监控发现率低,60%以上的问题还是依赖客户反馈、内部同学人工发现等
当故障真的发生时,故障的发现时长以及恢复时长都有待提升
所以基于以上几点原因,我们发现前端安全生产能力的建设迫在眉睫。
3.安全生产核心策略
谈到安全生产,这个概念,大家觉得它很大,和前端岗位离的很远。
其实,前面我们也说了,前端安全生产,要解决的其实就是:围绕前端研发全链路高质量交付这个问题,通过 一系列自动化流程机制,保障线上业务稳定运行。
作为一线前端开发同学,其实在我们平时的开发过程中,大家或多或少的已经在接触前端安全生产了,只是大多数场景下是点状的做这件事情,或者整个基建是烟囱式分布的。
下面我给大家介绍下目前前端研发链路中,业内常用的安全生产策略:
开发阶段
单元测试(通过率、覆盖率等)
静态代码扫描(eslint、csslint工具、或者基于webpack的一些插件)
CI持续集成
发布阶段
UI自动化回归,在发布时的卡口验证
灰度发布体系以及相应的监控指标
业务指标监控
线上阶段
线上巡检(发现关键链路的404、死链、天窗、截图比对ui异常等问题)
覆盖前后端的监控(页面运行时错误监控、业务打点监控、白屏监控......)
线上故障后的秒级回滚止血方案
以上这套基础设施,是目前相对比较完善的前端安全生产保障能力。
基于这些能力,在业务体量中等或者前端团队规模不大的情况下,是能够有效保障我们的代码被正确生产消费的。可是当你的团队逐渐庞大,业务高速发展,迭代很快的时候,必然面临以下两个问题:
发现故障
UI自动化工具是在代码生产阶段最有效的保障措施(前面我们也说过,通过ui自动化工具我们能拦截70%左右的故障),那么问题来了--你的UI自动化工具能持续正确work吗?
预防故障、故障处理
线上发生了故障,是否部署了监控,监控的告警是否有效?
面对故障,应急策略是怎样的,问题处理SOP标准化流程又应该怎么操作?
发生过的故障、复盘过的策略是否落地了,下次同样的问题真的不会再犯了吗?
要回答以上几个问题,总结起来就是--如何检验前端安全生产水位?安全生产如何防腐保鲜?所以,下面我将围绕前端流量回放&前端攻防演练来回答上面两个问题
3.1 UI自动化解决方案-流量回放
3.1.1 问题分析
在我们日常的迭代开发过程中,一个完美的可持续运行的闭环应该是
新增测试用例
测试用例通过
上线发布
功能持续迭代
测试用例失败
更新测试用例
可是,在我们遇到测试用例失败的时候,常常会遇到面临一些现实的问题,导致5->6的节点失败,比如:
时间紧迫,项目马上要上线了
代码不规范,没有使用纯函数,或者依赖的三方库不太支持测试用例
维护的代码作者不是我,逻辑太复杂,写用例成本太高
......
为了解决上述的这些问题,业内有诸多UI自动化解决方案,并且这些方案在不停的更新迭代,这些方案大致归纳起来,有以下几个发展阶段:
丰富的工具:比如puppeter无头浏览器、webdriver等
半自动化能力:通过UI 自动化录制工具,低成本的解决测试回归问题,业内领先的几个工具比如:
UI Recorder:https://github.com/alibaba/uirecorder
Selenium IDE:https://www.selenium.dev/selenium-ide/
Macaca:https://macacajs.github.io/zh/
全自动化比对
比如巡检平台,能够实现基于用户操作链路的用例执行、全自动化的图片截图比对、404链接、天窗问题等等
纵然有这么多优秀的解决方案,但是我们无法解决的痛点仍旧存在:
覆盖率:用例的覆盖率无法通过人肉的方式去弥补
维护成本:当业务快速迭代时,我们如何保障低成本的方式去维护这些用例
小流量场景:有些小流量、高成功率的场景,我们无法在灰度阶段依赖线上流量发现问题
3.1.2 核心策略
一句话解释流量回放:基于用户真实行为和接口数据的离线沙箱回放系统,用以在开发阶段验证代码正确性的工具
简单来说,如果我们采集了用户的数据、用户的行为加上当时的执行环境,通过下面的关系图,一定能够得到一个反馈的结果
3.1.3 系统架构
系统架构分为如下几部分:
用户行为采集:这里包括用户的点击、滚动、resize、输入、鼠标事件及聚合等
用户数据采集:用户的数据包括html、静态资源js/css、请求ajax或者fetch、DOM的变化
回放平台:主要是整合资源,提供api、调用服务等
回放沙箱:构建一个完全封闭的沙盒,去回放当时用户的操作
用例生成:基于采集的信息,处理后生成待执行的用例
回放验证:在回放的时候,我们用待发布的资源替换掉原来的用户数据assets,在沙箱里运行一次,再通过比对策略与算法,得到一个结果,这个结果能告知调用方,这次的发布,有无风险。
3.1.4 沙箱技术解析
流量回放整个系统架构最重要的部分就是回放沙箱技术,我们是如何做到完整回放用户的操作,又不对用户自身的数据造成影响呢?
比如,我们回放的是一次下单行为,这里一定有个动作是创建订单(提交接口),那么我们回放时候,既要创建订单,又不把数据真正发给后端。
下面我们来一起拆解下实现机制:
容器:整个沙箱的构建依赖于JWebDriver和mocha,这两块都是开源的技术,大家可以自行了解
资源代理:利用代理技术,高度控制沙箱容器的网络状态。在回放时候,比如访问阿里巴巴国际站的首页,这个请求是真实发出去的,我们必须通过代理的方式用【采集到的资源】替换掉原本应该【实时请求的资源】。同时验证的时候也一样,用正在【开发中的资源】,替换掉【采集时的资源】
异步请求的还原,上面说过,我们既要发送请求,又不把真实的请求打到后端,这里的思路就是拦截,利用service worker去拦截用户的fetch请求,然后修改response,最后返回给接口
下面有一段伪代码,大概描述了沙箱的结构:
3.2 安全生产长效防腐-攻防演练
前面我们说的是,如何发现、预防故障,接下来我们谈一下如何检验安全生产设施是否完备,以及面对故障发生时候,相应的措施、人员是否做好了准备,也就是如何用最少的资源提升安全生产水位。
3.2.1 定义前端故障演练
攻防演练其实大家并不陌生,但是谈到前端的攻防演练,大多数同学可能还没有接触过。
在阿里内部,服务端的攻防演练相对成熟,大体来说就是基于混沌实验有一套ChaosBlade实验工具,基于该实验验工具,沉淀了一个故障演练的平台。
总体来说,服务端的故障演练大致分为四个象限
单机分布式问题
软硬件问题
进程内外的问题
人为、流程不完善的问题
然而,前后端在整个生产、消费的链路上完全不同,导致大部分的演练能力无法复用。
所以,现在我们必须重新定义前端的故障演练:
基于混沌工程理论,在前端领域以可控成本在生产、消费环节注入故障,通过持续性的演练来提升系统、工具、流程、开发抵御风险的能力
3.2.2 如何设计一次演练
那么在做演练的过程中,我们如何设计一次演练?
整个过程分为三部分:
演练前的准备,主要明确演练的场景,以及评估下该场景是否可被演练。如果可以被演练,那么它稳定的状态(你可以理解为正确运行的状态)是什么样的?演练时爆炸半径是否可控,是否会对线上造成资损等等。
演练中的执行,演练中主要考虑的问题就是整个演练如何被初始化,以及初始化后如何自动化执行,是否能一键启。当然还有一些技术细节,比如流量如何被放大等等
演练后的复盘,故障发生后,需要能被复现,并且提供给红军同学可恢复的操作。演练后我们要考虑的就是,这次演练有没有场景沉淀,复盘结果是怎么样的
3.2.3 四大攻击武器
既然要做演练,必然需要一些攻击武器来帮助我们来实施攻击行为,从前端的整个链路来说,大体分为两部分
生产阶段
源码注入:源码故障注入从代码提交环节起执行注入,在仓库中通过机器人新增包含故障代码的分支,在工程链路中引导开发者发布该分支,并评估CR、发布卡口、监控、响应等能力的表现
CR变异:运行时对CR diff内容进行变异,评估CR的整体合规性和问题发现能力,对实际代码无影响
消费阶段
静态资源劫持:静态资源劫持即将预先准备好的响应状态设定在演练用的DrillCDN上,然后将隔离环境中的客户端对指定静态资源的引用劫持到DrillCDN,由DrillCDN给出指定响应。比如可以用来返回一个已预先注入过故障的静态资源文件。之后由隔离环境的流量放大机制触发演练告警,并通知到相应负责人启动应急流程。
日志注入:日志注入核心原理是通过演练平台下发上报策略后,客户端模拟错误日志上报对应监控平台,触发监控报警,开发同学启动应急流程
3.2.4 一次演练流程
那么一次真实的演练过程是怎么样的呢?
下图描述了整个流程,大概梳理一下:
角色:
蓝军:演练发起方
红军:被演练方
核心组成:
演练平台:负责中控配置、流程控制、任务调度、API服务能力提供等
安全环境:一个隔离的环境,可以真实的访问某个页面,并且提供篡改文件内容、接口请求&返回等能力
源码变异:上文3.2.3已描述
drill cdn(演练cdn):一个用于模拟静态资源状态的伪源站
告警触发:基于各自业务现状的告警机制
故障重现:安全环境产生的故障,真实能够到红军同学的本机进行复现
3.2.5 关键技术解析
关键技术解析,我们主要回答三个问题:
1、不同的监控体系,如何处理异常上报
前端监控多种多样,监控接入方式也各异,那么我们如何去触发这些告警呢?
其实,总体来说,前端的监控包括这两个部分:
运行时的监控
业务打点监控
我们的核心思路就是:让页面自己执行起来,走原始通道告警。举个例子:如果我们想注入源码的问题,"a is not defined",这里我们不用管页面接入了何种监控,他是如何捕获这个异常的,我们只要把对应的代码问题注入到页面,在安全生产环境执行这个注入后的页面,运行报错后,自然会被监控所捕获上报。
2、如何产生足够的量,让监控告警
这里有两个思路:
通过webdriver云,同时触发多个浏览器执行你的错误页面
通过研究监控上报策略,找到上报API的采样次数或者采样率等参数,修改这些放大参数来告警,举个例子:正常的告警是上报1次,采样率是100%,那么你修改上报次数100,也就相当于100倍的放大。
3、故障触发后,如何复现,并恢复
任何的故障演练,最终还是要做到让红军同学“有的放矢”。触发的故障,要在红军同学本机上能复现,这里的核心思路就是,把错误的代码落到DrillCDN上,红军同学只要绑定这个机器的ip就可以复现了。或者利用你自己业务的架构特点,直接发到预发或灰度上,红军同学绑定响应的ip就可以复现了。
总结
整个前端安全生产绝对不止我这篇文章说的这么简单,里面涉及到生产、消费的各个细微环节,我这里主要从两个小切面带给大家一些前端安全生产的新思路
总体说来:
依赖流量回放来做故障的自动化发现&预防。通过线上sdk接入,采集用户的行为、数据、执行环境后,在预发环境来检验新的代码是否符合我们的预期。
依赖攻防演练,用最少的资源,在整个开发的链路上,检验各基建的有效性、锻炼开发同学的故障应急能力。同时,也期望攻防演练,能够撬动更多业务侧的同学来一起做好具有自己业务特性的稳定性基建。
关于本文 作者:@陈波 原文:https://www.yuque.com/binfe/cquxg7/tspizt
为你推荐
【第2088期】前端中台化,把格局做大——NodeJS 和测试服务探索
欢迎自荐投稿,前端早读课等你来