一、自序
前些时候我讲了点“欺骗防御”的事情,并且预言了它的发展趋势。没想到,突如其来的一场全国范围的攻防演练让它爆红。在攻防博弈中,防御方第一次抢到了一点先机,给攻击者带来了一些不确定性。我相信,随着这次演练,欺骗防御产品将会得到更加普遍的使用,继续改变后续的攻防模式。
这让我有些高兴,写点东西也许真的可以给安全行业带来一点帮助。那么,我就再多写一点,聊聊安全开发——其实这也是我创业后做产品的先后顺序。写这篇文章,希望除了帮助攻防,还能帮助到打算准备创业的人——了解一点关于“做什么”的决策思路和方法。
二、为什么重谈安全开发
微软大概在2004年左右就开始准备安全开发体系(SDL,Security Development Lifecycle)方面的理论框架,2008年正式公布了第一版。距离今天10年了,对于IT行业来说,相当于已经过了1个世纪。为什么我要在2016-2017年之间去搞一个老掉牙的东西?简单地说,因为行业变了,需求来了。
微软SDL安全开发体系
这几年,大家都在说云计算给很多行业带来了改变,我想借助一两个例子,把具体的变化是什么讲讲。
先看银行。早几年,我们要转账要汇款,必须拿号然后在柜台排队,银行的工作人员操作他们的业务系统帮我们完成交易,旁人根本接触不到银行的业务系统。但随着现在业务的云化和互联网化发展,银行有了手机银行,有了微信银行各种互联网业务,每个人都是自己操作业务,每个人都可以触碰到银行的业务系统,这里面也包括黑客。说难听点,早几年银行的业务系统就算有漏洞又怎么样?黑客很难进行攻击,但是现在业务直接对外开放之后攻击面就大了,他们对安全开发就有了更高的要求。证券行业也是类似。
另外一个有意思的行业是智能设备。这几年关于摄像头、路由器等设备的漏洞研究不少,但是是否有很多新的漏洞?我看不多。更多的是HTTP请求里面输入命令直接执行,是CSRF,是栈溢出,是登录绕过。这些行业,原本是一些偏制造的行业,但是现在突然互联网化了,功能复杂了,对所有人开放了,互联网业务WEB那一套漏洞,要重新走一遍了。
其它受到互联网化云化的冲击,业务模式发生很大变化的行业,还有很多。5G到来之后,永远在线的系统会更多。这些都是需要人去帮助建立安全开发体系的对象,否则就只能在网络蠕虫的威胁面前瑟瑟发抖。
所以我们去搞了SDL这么一个看起来老掉牙的东西。
三、为什么是我们来搞
那么多的大安全公司,很多人可以讲,为什么是我又出来呢?SDL这东西,核心关键是落地,结合立项、开发、测试、发布整个流程体系来落地。一般来说,大的安全公司可能对这个不是太了解,容易搞成虚的。
相比而言,大型互联网公司的甲方人员最了解SDL,他们讲最合适。但是他们没有来讲,那么从大型互联网公司出来的我就比较适合搞这个事情,有经验,了解安全和研发的关系,了解立项、研发、发布的整个体系。我记得2006年时候,B2B主站、淘宝主站XSS漏洞都是数以万计的,但是在严格的SDL体系下很快得到了根治,阿里现在是国内安全做得最好的企业之一。
四、最佳实践
4.1 SDL的本质
SDL的目标主要是两个,增强安全性和降低成本。某种意义上,如果将“增强安全性”当作必须满足的要求,那么降低成本就成了SDL的唯一目标了。这里的成本主要是漏洞修复成本。
漏洞发现得越晚,修复成本越高,如果是线上运营的系统被爆出漏洞,修复成本除了下线、修复、重新上线导致的业务损失成本、人员成本之外,甚至还包括了消除公关事件影响方面的成本。所以需要在产品上线之前建设安全开发体系,尽可能早的发现、解决安全问题。最直观的做法就是安全部门在产品上线前拿漏洞扫描器扫描一下,有问题打回去改造。
这里其实就是SDL的本质所在,安全前移。
完整的开发体系,可能包括产品需求设计、架构设计、编码、测试、安全检查、上线运营等几个步骤,更早些年是用漏扫扫描线上系统,后来意识到了越早解决成本越低,就在上线前扫描,往前移了一步。继续深入下去,安全还可以进一步前移,上线前安全审查期前移到QA测试期,前移到开发期,前移到产品需求期。
SDL方案架构图
安全前移的挑战在于目标群体其实是不懂安全的,比如QA,比如研发,这就要求安全部门通过一系列产品赋能给他们。相对应的安全产品,可能是黑盒扫描(DAST:动态应用安全测试)、灰盒扫描(IAST:交互式应用安全测试)、白盒扫描(SAST:静态应用安全测试)、威胁建模,以及全流程的开发安全培训。
总结起来SDL的本质就2个关键词——前移、赋能(赋能是实现安全前移必须的条件)。
4.2 如何落地
安全部门想落地SDL,要永远记着一句话:SDL项目不是安全自己的项目,是安全和产品、研发、QA一起的项目。做SDL的基调,得先明白这几个部门之间的关系,再定下来。
很多时候,研发和安全是对立部门,插句题外话运维部门和安全部门也经常是对立部门。阿里曾经就有人抱怨过安全部门是东厂阉党,权势熏天只管自己业绩不管众人死活。这并非完全是低级吐槽,确实存在安全人员动一下嘴运维和研发可能就要跑断腿的状况,改代码、打补丁回归测试等等一大堆烦心活要干。因此,挑战安全部门,打脸,很多时候是大家乐意看到乐意干的事情。
另外,企业里面多半有了完整的已经使用了很多年的开发测试发布工具链以及平台。如果安全人员想把研发和测试从原有体系里面拉出来,是一件非常困难的事情,更别说在拉出来是因为安全部门想“领导”他们去做一些“原本不属于”他们的工作,比如说安全测试。不能期望每个人都能够站在足够高的高度,去理解安全理解整体业务。
所以做SDL的基调就是柔和低侵入、低误报多建议。柔和是指一定要依附在企业原有的开发测试平台来做,安全相关的产品接入SVN接入Jenkins接入Jira,支持LDAP等第三方认证,努力去适应他们的体系,减少需要对方改变或者操作的地方。同时,安全产品要将误报控制在非常低的限度,并且提供非常简洁清晰干脆的解决办法,这一点也是能把安全赋能给QA和开发的先决条件。说难听点,误报了研发和测试都会感知到,马上来找麻烦,误报也确实也影响到了他们的本职工作。漏报了他们不知道,也不会影响他们的工作,而且即使知道了也不会主动出来找安全部门打自己。
基调有了,那么,威胁建模、SAST、IAST和DAST等4个产品,怎么落地效果最好且最经济就非常清楚了。我的判断是先IAST再SAST然后威胁建模。至于DAST可以不放进SDL项目中,而是由安全人员自己运营,检查线上运营中的业务。
4.2.1. IAST
先做IAST是因为相比SAST它的误报更低,相比DAST,它实施使用更简单,扫描更全面,而且和QA原本的工作结合更容易。我认为IAST最好需要提供4种接入模式:代理模式、流量镜像模式、插桩模式以及日志平台模式,确保能够适应企业复杂的业务场景。其中代理模式和插桩模式是必需的。
在最常见的代理模式下,DAST爬虫难以爬取的某些ajax页面、JS拼接链接、登录后页面通过代理比较简单就能扫描到,更关键的是IAST扫描对业务逻辑覆盖得全面。举例说,一个表单要填写手机号码然后提交。DAST能识别到表单但是它不知道这里是要手机号码,只能填写随机的字符或者数字,服务端检查不是手机号码,那么是手机号码的那个业务流程是永远无法覆盖到的。而QA原本就要手工或者自动化点击链接,测试功能,覆盖所有业务分支,他们会测试填写手机号码和非手机号码的情况,IAST会捕获到这两种情况下的流量然后进行攻击变换测试,发现各自的问题。
其次,在代理模式下,多个QA人员进行安全测试,IAST可以通过智能化自动身份权限识别、交叉访问的方式,检测水平越权、垂直越权漏洞。这种业务逻辑方面的漏洞,基本上就是DAST的死穴。
插桩模式是代理模式的反面。代理模式需要配置客户端设备,而插桩则是配置服务端设备,一般由运维人员把agent统一分发到测试服务器,或者在devops流程中打包进去。某些场景下,代理模式比较难以适应,比如说双向HTTPS加密、内容自定义加密、不接受脏数据、存在csrf token、存在大量验证码等场景。插桩分为主动扫描和被动扫描,个人觉得污点传染这种被动模式更优。如果涉及到测试时候的脏数据等问题,污点传染模式就是唯一的选择。
目前来说,JAVA、.Net、PHP、NodeJS、Python都有比较成熟的插桩方案。插桩的关键是性能和稳定性。可能需要在runtime里面插入万级别的hook点,截获数据的输入和复制传播,少一个点就会漏掉大量的漏洞。Hook点多,容易影响应用启动时间,降低性能,影响QA人员的工作体验,这里就是核心竞争力所在。
另外几个模式比较简单,主要是为了更好的集成性。如果想要更好,还可以提供WEB API,方便QA人员写脚本做全自动的测试,和DevOps流程整合得更紧密。
通过部署IAST产品,QA完成本职工作的质量测试时,IAST系统自动完成一份安全报告,相当于一份时间完成两份工作。然后两份报告由QA直接提交给研发整改,减少了安全部门这一个环节,提高效率。基本上,IAST部署完成,就可以将应用安全做到80分。
4.2.2. SAST
SAST最大的问题就是误报和漏报同样突出。一个系统,有20个漏洞,SAST扫描一下可能报告100个漏洞,其中10个是真的漏洞90个是误报。在大部分公司里面,以现阶段这种研发与安全的关系,肯定是用不起来的。另一个角度,SAST可以发现的这10个漏洞,IAST一般也能发现。这么看,SAST似乎毫无价值,是不是不要做?不是,还是得做。因为SAST可以把漏洞的发现和修复闭环在研发阶段,连QA都无需介入,是将安全继续前移了一大步。那么关键问题就是怎么做,既能够保留成本低的优势,又能去除误报漏报带来的危害呢?
AI肯定是一个方案,哪一天AI看代码像最有经验的安全研究人员一样,SAST就成熟了。在AI不靠谱的时候,我的想法是通过体系来解决。目前最靠谱的SAST,应该以误报率为核心指标,通过多漏报的方式来减少误报,然后逐步调优,在控制误报的情况下,逐步减少漏报。这样,SAST产品才能真正用起来。简单说,20个漏洞,SAST报告12个漏洞,里面有8个是真的,4个是误报。这种情况下,研发可以在不找安全的情况下内部消化掉。漏掉的漏洞怎么办?后面还有IAST,它来补足。
相对于甲方人员来说,单产品的误报是万恶之源,而漏报则可以通过体系来补足。就像一块木板有1个洞,第二块木板有2个洞,第三块木板有1个洞。当我们把三块木板叠加到一起变成一个体系,每块木板的洞位置不同,互相补上,体系是不漏水的。而误报是没救的,一旦产生,在当下就需要耗费资源去处理,用出去了的资源是收不回来的,就像在清水里面滴入了一滴墨水。体系的力量就在于在每个阶段做每个阶段最擅长的事情,强行在一个阶段解决所有问题就会事倍功半,甚至完全没法工作。
所以我认为,当前阶段直接部署SAST除了合规之外,对安全并没有实质性的帮助,意义不大。一定要以IAST为基础,SAST作为锦上添花,把一小部分漏洞提前到研发阶段发现并修复——虽然这部分漏洞IAST同样能发现,但是越早发现修复成本越低。
漏洞修复方案,尽可能的细化,不仅仅是字句描述,而是要带代码示例。最好的办法是SAST自带安全组件库,可以是独立的库,能够和用户使用的框架继集成更好,这样修复方案可以更明确,细化到函数名称。不然修复方面,可能还是需要安全人员介入,目的就没有完全达成。这个组件库,在安全培训的时候做讲解的基础,安全培训没有明确的步骤,任何时候都可以开始,而且是要长期的周期性执行,贯彻整个研发体系。
SAST实质性的工作,除了代码安全检查之外,还有很大一部分在软件成分分析(SCA),也即供应链安全。研发写代码可能使用某一个开源的第三方库,这个库又引用了另外的库,最终给产品带来未知的不可控风险。SCA要能够自动化识别这些第三方组件,并且和漏洞库对接,发现问题。如果再扩展一下产品风险的范畴,还能够包括第三方库的license风险,比如说程序员无意识的引入了GPL3授权的组件。
SAST的部署,一般有两种模式,IDE插件和代码仓库集成。基于前文的柔和低侵入,我毫不犹豫的会选择代码仓库集成模式。研发人员完成一天的工作,上传代码然后回家了。SAST产品检测到代码仓库里面的变化,晚上进行安全检查,第二天程序员打开电脑收到邮件,告诉他昨天的代码有几个安全问题,有什么风险,用实例演示应该如何修复。整个过程对于程序员来说完全是无感知的,什么额外的投入都不用,SAST产品用起来如喝水一样自然——这里也是为什么我极力抗拒误报的原因,否则程序员收到说有100个漏洞的邮件,马上怒火冲天提着早餐就来安全部门了。
4.2.3. 威胁建模
安全再往前移一步就到了产品设计环节,对应的安全就是威胁建模。其实以现在的技术水平,安全越往前移,控制力度越差,对产品最终的安全提升越小。IAST是效果最好的环节,SAST是锦上添花,威胁建模就是花上再添花了。
通过问答式表单,让PD选择产品功能需求,系统自动生成一份安全需求文档,然后架构师依据这份文档来进行安全设计。问题在于生成的这个安全需求文档是纯粹的指导性意义,没有任何可以自动检查的手段,容易流于表面。
所以建议是最后做,不缺钱的时候,有总比没有好。
4.3 简单总结
SDL方案要柔和、低侵入,不改变研发和QA的工作习惯,不增加他们的工作量,认证、使用方式都要接入原本的研发流程系统,不能另起炉灶。安全测试优先控制误报,允许漏报,所有的修复建议和方案要以安全组件库为基础,细化到用哪个函数的程度。
先上IAST,把安全做到80分。然后上SAST,锦上添花做到85分。安全培训贯彻始终,基于安全组件库来讲安全,做到有的放矢。最后,有钱可以搞搞威胁建模。