汤青松 ,北京趣加科技有限公司 安全工程师,实体书《PHP WEB安全开发实战》作者,擅长企业安全建设,SDL安全建设。
PHPCon 2020 第八届 PHP 开发者大会分享《PHP安全编码规范与审查》,NSC 2019第七届中国网络安全大会分享《PHP反序列化漏洞分析实践》看雪2018 安全开发者峰会担任Web安全训练营 讲师
大家好,非常高兴给大家分享《代码安全体系建设》议题,我是汤青松,目前在 SDL 方面做的比较多的。今天讲的这个话题其实和 SDL 有很大关系的。我这次分享这个话题的其实就是 SDL 当中的一部分。很多同学如果在甲方也会去做 SDL 当中的一些工作,所以我希望我这次分享的内容对大家有所帮助。
这次分享的话题是 SDL 当中的一部分,但是它不完全是 SDL 因为我主要还是聚焦于在安全向左移的这样一个概念当中。所以今天分享的话题主要是聚焦于这个代码风险管理。那在这个代码安全当中可能有哪些风险点了?那它可能会包含技术方面的工作和非技术方面的工作,比如说管理工作以及这个学习方面的一些事情都会有讲解到。
我今天讲的这个话题,主要还是在于意识、技术、监督和学习四个方面怎么去开展我们的安全工作?这里我做了一张脑图,那在脑图当中,我觉得从这个淡淡安全体系当中,我们有四个层面了可以去做的,比如说这个安全培训。
在安全培训当中,我们第一点要告诉他有哪些风险点,第二个就是我们得教他怎么去避坑,那么教他避坑了,我们就可以直接拿到他这个仓库的代码,然后我们自己先去分析一遍,分析完之后,然后在培训的时候我们就可以告诉他你这个代码了哪里会存在一些风险点,把这个问题告诉他。
第三个就是我们告诉你了,你不能怎么去写,你应该怎么去写。那么我们制定完这一套规则了,我们不可能说其实人工去盯着您得有一套监督机制。这里我也会给大家去讲一下,怎么去结合semgrep 和GitLab做一个钩子事件,实时检测这个代码当中的一些风险点。
那么第四个就是我们在上线前肯定会有安全测试。在安全测试当中可能会有哪些疑难点?那今天我大致会提到这样几个问题点。那首先我们来说一下安全培训我们怎么去做。那安全培训我相信很多做技术的同学,他可能就是自己的技术了是比较好的,但是让他去给别人讲怎么去踩坑以及一些案例,她不一定是他擅长的。
我们先来说一下我是如何理解保障整个应用安全的,我认为它不是由某一个维度是否做得足够好来衡量,它是一个综合的方面。也是多个团队配合起来的一个工作,我们作为安全人员在这里要负主要责任。
我们要想尽一切办法去和开发和测试一起保障保证应用安全。我们首先给这些开发人员建立这样一个安全意识,那告诉他了互联网当中会有很多的一些漏洞,那这些漏洞了有哪些危害?那首先在他开发的时候,他就会想到不能让这个应用有漏洞。
第二个让他有意识之后,要教他怎么去避开这些坑,不要让他知道有这些安全风险起来,他不知道怎么去处理,然后还是踩坑了。那么这个时候我们安全人员自己在这个技术方面了,要有一定的这个能力。
第三个在监督层面。如果说你告诉开发人员互联网当中有很多漏洞,也教他去避坑了。但是没有监督很多人了他可能并不一定按照你的要求去做,所以这个时候您得有一定的监督机制。
第四点是是通过事件驱动,我相信作为甲方,你肯定还会遇到一些安全事件去驱动。比如说我们公司是做游戏的,还时不时会有一些拖机挂或者等等一些外挂这些方面的问题。那么我们会把这些事情给整理起来,整理成案例,再给这些开发人员去学习。
所以这里我讲几个非技术性的一个话题,就是我们怎么去给这些开发做培训。那首先我觉得做培训有这几个方面,我可以给大家一些参考意见。比如说首次的这个培训我们讲什么话题?怎么避免一次性把这个话讲了,那么我下个季度还讲不讲?是不是我一次性了就把话讲完了我觉得都是有一些技巧的,就是你第一次该讲什么东西。
第二个说我们培训了是,一次培训了,我们就把所有的这个开发组成员了都叫上。那么比如说几十号人,上百人都叫上,然后我们自己在一个舞台上去讲还是这个小组去讲了。那这里头我是推荐大家小组讲,那一会我会讲到为什么去小组讲。
第三个就是案例就地取材案例了,我们一定要在每次培训之前把他那个代码打开去看一下,拿着他的代码给她们做一完培训。然后接着了我们就开展我们这次要培训的一个话题,那这样的一个形式。
首次基础培训了我们可以给他讲哪些东西了?我觉得你首先可以给他讲一下你在这个甲方安全当中了,你是怎么去给他们做代码审计的,你是怎么去做安全的?这个其实是她比较关心的,让他了解你的工作。那你平时会关注哪些点,你给他充分交流一下。那么建立一个互信机制。
第二个你可以给他讲一下这个漏洞分类,比如说了通用的一个编码型漏洞,比如说SQL注入, XSS csrf 文件上传,然后命令执行代码注入等等这些问题。那把这些问题了给讲完之后了,我们还可以给他介绍一下这个逻辑性漏洞,比如说这个支付漏洞、越全漏洞、验证码漏洞、短信漏洞等等这些漏洞了,我们可以给他科普一下。
那么讲这些漏洞的时候,你可以去结合他们这个小组了,他们是做哪方面业务的?比如说做中台的了,他可能会脱离业务。他可能没有这样一个支付的问题,或者说没有用户的一些问或等等和他这个主无关的一些问题,可以给大概提一下,但不要讲细了。
第三个你可以给他讲一下代码自查的一些方法,你可以教他一些简单的方法,比如说他写完代码之后了,怎么去审计自己的安全问题?但这个参数有没有做过滤类型,是否强制的,比如说我要介绍一个 ID 那在 PHP 语言当中了,那可能他没有使用这个整型转换,那么接受了有可能这个字符,那么拼接到这个SQL注入当中了对吧?那这个时候你就要跟他讲,你说你在这个接收的时候你得做一次过滤,你如果没有做过滤了,在拼接社会语句的时候你得做 pdo ,然后查 SQL 大致教他一下怎么去查。
那这个SQL漏洞怎么查了?我相信做甲方的技术同学都有一定的见解,在这里我就不展开了,你可以给这些开发的大概提一下。一般来说就是做开发的同学了。自己对这一块也是比较了解的,你给他点个开头,他自己很多事情他能想明白的。
第二个就要在做培训的时候了,我的建议是小组培训,比如说你们一个公司可能会分为很多个小组。那么我目前来说我主要是给后端的同学做培训。所以我一般做培训的时候我会讲后端的一些东西。那么后端他每一个组了他其实涉及的东西都不一样,比如说有些组了他不会用到 HTTP 协议,他就会用这个 socket TCP 这种协议。那么你给他去讲一些 web 业务的一些漏洞了,他可能不爱听。
所以做小组培训的目的了,我觉得主要就是因材施教。那每个组他有自己不同的一些特点,你得给他讲他自己不同的。那么你尽量在每次培训的时候,你尽量控制在 10 个人以内。而且我觉得时间你要控制一下,就要控制在这个 45 分钟以内。们平时上课了对吧,也是 45 分钟以内。
说了你要有这个时间的一个意识,你不要一直讲,讲完之后翻其他人压根就没来听,你要有以这样一个互动机制。那这里有三点了,我需要给大家去说的。就第一个了就是开始讲到了,你一定要贴近这个团队的一个真实代码。你每次给他做培训的时候了,你得先把她代码审计一遍,审计完了多多少少可能会有一些安全问题,或者说有些不规范的问题。你拿着这个代码去跟他说,你不要直接上来,对案例那样不是不好的,很多人可能就不乐意听。那么第二个就是要贴近他的业务场景,刚才也讲到了,那做 TCP 的你给他讲 HTTP 他不乐意听他,我压根就没有这个业务,你给我讲啥呢?第三个就是多分享故事的一个形式去形成一个互动的气氛。比如说你在给他做培训的时候,你给他讲越全的时候,那你就讲以前了,可能你挖过某个网站的一个预存漏洞,你是怎么发现的,以及你可能在网上了看到过别人的一些案例,那这些分析步骤了你可以给他讲一下。
你比如说我发现了这样一个 URL 的 ID 等于100,那么我觉得想试一下,我把这个 100 了改成 101 在 URL 地址栏当中回车一下。发现看到了别人的一个信息,也发现了别人订单信息,发现了别人的一个个人用户信息以及其他的一些权限信息,多以这种故事的形式给他去交流,我觉得是效果非常不错的。
第三个就是案例就地取材。那很多时候我们给 A 组的同学去做这个分享的时候了,那他不一定了有他们这个组的一个漏洞案例。这个时候我觉得你一定是就近的,你可以讲他们部门的对吧,可以给他讲这个整个公司的。然后这个案例就是尽量靠近他们这个团队。那么你怎么取这些案例呢?我们知道在 2016 年以前,这些互联网当中有很多这种漏洞案例,包括各个大公司的一些漏洞案例都是有的。但在 2016 年 6 月 1 号之后,我们基本上看不到了,为什么网络安全法出来了,所以很多这种比较直观的漏洞案例了,我们现在没有办法,说很轻松的拿到了。这个时候了,你可以从三个渠道取了。
第一个说你在平时做代码审计的时候,你把这些案例了给存起来,截好图。然后把敏感信息的打个码。但是把意思大概说明白,把这些案例给整理起来。第二个就是从安全测试当中,我们每一次业务要上线了,肯定会有一轮安全测试,把业务测试当中发现了一些问题点,也可以整理起来,我们是有一个案漏案例库的。第三个就是漏洞事件了。那比如说一些通用型的一些漏洞事件,像那个 2021 年年末的时候,是不是有个 logic4 的这样一个组件,我记得在快过年的前几天了,那个时候出了一个命令执行的漏洞,那影响范围还蛮大的,那可以把这些事件了给它整理一下。你可以告诉他说你依赖了一些组件了,要及时升级。如果了你要引用一个组件了,你要最好查一下它的版本有没有漏洞,有漏洞的话你就不要使用了。
一般来说我们 php会用 composer , Java会用另一个包管理器以及 Python 都会用包管理器,那么我们要及时去更新一下,不要说我们打包一次,几年我们都不更新,那样很容易就会产生一个安全漏洞。
那接下来我们要讲的就是风险实时提醒。那么我们给他做完培训之后,我们有这个首次培训,也有这个每个季度一次培训,也有这些案例的一些讲解对吧,我们还得有这个监督的一个作用,就是他们写完代码之后,我们得及时去提醒他,以及了我们每一个季度了我们有一次全面扫描等等这些信息。
那首先我讲一下这个风险提醒的一个作用,主要就是强化大家的一个提醒的意识。你不要讲了之后就不提醒了,过不了一周两周,他之前的代码习惯了,该怎么写还是怎么写?你会发现你之前的培训白讲了,你给他讲了之后了没有太大的改变。所以你要有提醒。
那实时提醒了我们可以在 git 的仓库当中做一个勾子事件,每一次它 push 代码的时候,我们可以把他的代码了给拿出来,拿出来之后提取他改动的行,提取改动出来的行了。然后我们来判断一下他有没有危险函数等一些问题。那如果有这些问题了,我们就给他反馈一下,就告诉他你哪个地方了可能会存在危险。
提醒这件事了有三点意义。第一个就是加强他的安全意识,让他知道这个安全事情有人在管。第二个就是从源头当中去阻挡安全。但这个钩子你不要说遇到这个危险函数了,就直接给打回去。你可以在这个 git 当中给它返回一下,返回一个提示信息,告诉它这个地方可能存在风险,让他去注意一下。比如说在命令执行这个地方你放了一个变量,那么你需要确保你的变量是可控的,是过滤了的给他这样一个提示作用。
第三个就是提升一下安全的反馈速度。那如果说你没有这样一个实时的提醒了,你每个两周去给他这个仓库全面扫描一次,那可能他代码都上线了对吧?
风险函数我简单列了几个函数,比如说这里了有这个代码注入的,有这个执行系统的命令的,有使用这个明文 FTP 下传文件的,
还有一些加密库以及一些正则库,那这边还有一些信息泄露的提醒。
你都可以放到你的这个安全风险提醒里面去。我觉得优先级了,你可以把这些高危的了给大家放进去。比如像 FTP 的,你可以看情况,你放不放都行。那像 pprof 了,我觉得你肯定得注意。那 PP info 里面还是有蛮重要的信息的以及 Golang 里面的,它直接使用了裸写了这个语句以及读取文件内容以及执行系统命令的这些危险函数了,你都给它去提醒一下。
那刚才说的这个勾子事件了怎么去使用了?那其实这个勾子的原理主要就是在这个 git 服务器当中了,存放一个勾子的这这个脚本,每次他 push 的时候了,服务器他会触发这样一个脚本。
触发脚本的时候,你就可以通过一些命令行了,你去获取到他有哪几个文件有改动,那改动了一些行号了,你都能拿到。完了这些数据之后了,你把这个检测的规则就刚才我提供的一些危险函数,以及你自己去拓展一些,把这个规则文件给它写好。
第二个,这个semgrep现在已经比较流行了,已经有很多这个团队在使用了,所以我觉得也是一个比较成熟的东西,你可以通过 sum group 加上这个规则去检测这个代码,然后把这些风险给返回回来。那么具体的实现地址了,我之前写了一篇文章,还是比较详细的,那大家可以去打开,然后按照操作了去实现就行了。
那这里我给大家展示一下这个钩子了,它会是一样一个什么样效果?比如说我在命令行当中,我输入了一个 git commit 然后提交这个代码,然后在推送的时候它就会触发。那么在推送的时候,我们可以看到他这边告诉我哪个文件。那么它的行号,当中有个 EXEC 执行了这个 git 当中的 A 这个变量,那么它有可能会导致一个命令注入,那一定要确保内容不是用户随意控制的。那么这样一个提示性的话语,那具体了你可以去把这个界面了给它优化一下也可以。
在代码审计当中我们也有四点可以给大家去分享一下的。
那首先我们代码审计的方向,我们要怎么去审计呢?那这里就有一些偏技术型的话题了。那么有一句话叫文无第一,武无第二,相信做技术了也一样。
那么第一个就是通用编码型这这个审计。那比如说我们可以审计这些这个sql注入、 XSS、 命令执行、文件上传等等这些,那么我们可以通过通用型编码升级出来。那么第二个我们得结合她的业务去审计。那比如说你只有有用户的系统,你才有这样一个用户密码找回以及一些权限方面的一些漏洞。
第三个就是组件型的一个,那你要根据这个仓库所使用的语言,比如说 php 的,那用compose ,那 Java 的了,那用另外一种形式。那么把这些组件了,确定一下它使用的这个组件版本,然后来确定他是不是有几个组件漏洞。那具体了通用型编码审计方法了。前面其实我也简单提了一下,其实主要从这三个方面去看吧。
第一个就是接收参数,如果说我这个地方接收了一个 ID 我明确知道它是一个整形的。当我接收的时候了,我没有用整形去强制接收。那在这种弱类型语言比如说 php 里面比较常见,那么你就可以去追踪一下这个变量,有没有放到sql语句里面去执行,有没有去返回到这个前台,有没有可能放到这个命令执行里面放在这个代码执行里面等等。这些方式就是追踪接收的参数,它没有过滤,我们就一直追到底,一直到这个程序结束。那么这是一种方式。
那么第二种方式就是关联词反常。关联词反常了主要说我们可以去这些函数的地方看一下它里面放的是不是变量,如果放的是变量,我们就追踪这个变量的来源。那如果说这个变量是接收用户的,并且没有做过滤,然后放到这个危险函数里面来,那么一定是有安全问题的对吧?第三个就是我们去解析它的依赖文件。那么目前来说就是 PhP 了, Java Python 以及 go 它都有依赖包管理。所以我们现在去检测它的依赖其实是比较好实现的,我们这里都可以去做一个。当然有一些它可能还是比较传统的方式。比如说 PhP 在老版本了,在 PhP 7.0 以下了很多这个系统了,他并不会使用 composer 他直接把这个源码了直接下载到他的这个目录下。那这个时候你解析可能会比较麻烦。
那么这个时候呢您得借助一些第三方的一些工具去分析出来。说到这个代码审计的一些工具选型了,目前前这些我大部分都用过,比如说 fortify ,用的是比较熟练的。那么 check max 了这一款工具了,我还没用,因为还没有买他们的第三款这个代码卫士的话是那个奇安信的。那这一款我倒是用过一段时间。
第四个了,我目前用的是比较多的,主要是用在这个钩子检测当中。审计系统当中也用到了它的一些功能,但目前来说就是全量代码审计了,我还是比较喜欢用这个fortify。那么在这个钩子事件的时候喽,因为fortify带它是那个 AST 的这种语法分析,它会比较吃内存,而且反应速度比较慢,所以目前主要还是依赖与这个 Sem group 那么第五款这个 CodeQl 的 CodeQl 了这款工具了我前段时间还在了解,还没有用到这个生产环境当中,所以我也不太好说。
第六款工具了目前来说我用过。的感受就是它是一个完全开源的,包括他的这个规则,包括他的这个引擎系统大家都开源了,但他只能目前来说能检测 PhP 其他的后端语他不能检测。所以目前我用的这个 fortify 和 Sem group 比较多一些。 fortify 就是要商业的和check max ,其实代码卫生也是商业的。 fortify 我目前来说还比较好用,checkmax没用过,没有太多发言权。
这个 fortify 泛了我用过一段时间,去年十二月的时候。用过一个月。主要的感受就是它和福利范检测出来漏洞数量是比较相似的,但它的界面设计了点击起来我感觉很不顺畅。据说这个今年 3 月份会有一个新版本,到时候大家也可以去试用一下他们的 SemGroup ,他的规则是 开源的。然后它的这个引擎了它是加密的。
第五个了 CodeQL 了。那 github 当中就已经大规模使用,当然也可以去体验一下。但是你只能用于学习,不能用于商用。那现在我再来说一下就是批量代码审计的一个实现方式。那目前就是几款产品,其实它对单个单款库的支持都还比较好。我在做甲方的时候会遇到这样一个问题,那因为我是要对整个公司的代码库的安全性了负责的,所以我不能说就检测几个仓库的一些安全问题点。那时候我得批量去审计。那像我们公司可能有 600 多个仓库,一个一个打开,我得疯掉,像 fortify 打开一个仓库大的项目,它可能得一两天。光审计起来可能得花个一年时间就让他分析完。所以如果用这种默认方案其实不是太现实的。
所以我写了一个批量代码审计的一个工具,就是QingScan。那么它主要的作用它其实是包含了四部分,一部分就是信息收集,第二部就黑盒检测,第三部分就是代码审计。那还有一个专项利用。那这里我主要提一下它这个白盒审计。那白货审计它其实主要的目的就是把你的这个项目了给拉下来,然后调用这个 fortify 以及这个sem group还有其他的一些代码审计的一些工具了,对它进行逐个扫描。那么扫描完这个,然后接着就扫描下一个。那么也可以分布式部署。那目前来说我已经在我们公司使用起来了,也推荐给大家去试用一下,那么这个地址在这里
https://github.com/78778443/QingScan
再就给大家分享一个安全测试当中,安全测试主要有这几个, web 站点测试、API接口测试、私有协议测试和案例输出。
那这个web站点测试了其实我觉得还是比较常规的,目前也没有什么太大的一个技术难点
比如说测试个sql注入、 XSS 但一般目前来说是个sql注入和 XSS 这些问题点已经比较少了, XSS 可能是反射型 XSS 多一些,但是影响我觉得也不是太别大,因为现在都是那个cookie 加密的就是HTTP Only 另外这种形式,所以没有太多可说的。
API 接口怎么测试了? API 接口它和这个 web 站点它有一些区别。像 web 站点我们可能可以通过爬虫的方式把那地址都爬虫出来,然后再对这个地址进行扫描。那扫描有结果了,我们就把这个验证一下,验证完之后然后提出来。
那么 API 接口有个问题点,就是我们没办法去爬虫。所以这个时候了我们通常打开一个xray的一个端口,用这个服务模式。然后手机了我们把这个端口指向xray的这个代理地址,然后我们去打开一些请求,把这些地址收集起来,然后去扫描。同时我们还会有一个 URL 地址的清单,就是这个地址其实是开发那边给我们提供的。然后这个功能测试那边同学也有一份我们在对这个地址了去进行这个逻辑进行的一个测试,比如说越权以及这个支付漏洞等等这些方面的问题的检测。
第三个就是私有协议测试,那私有协议了其实测试起来是比较麻烦的,比如说这个socket的协议,那么 TCP 协议,那么我们其实是没有办法直接把这个数据包了给解析出来的,除非了我们得有一个模拟他们的客户端。那目前来说只有几个就是重点项目了,我们会和他们对一下这个端和服务端的数据的一个格式,然后进行模拟测试。那这个工作量还是蛮大的。所以这个私有协议了,看各位的这个人手够不够了,没有太多好方法,只能去模拟这样一个这个私有协议的客户端。
那传统的外部站点测出来是最简单的,无非就是先收集地址,然后测试一下常规有没有问题点,然后对这个业务功能进行测试一下。那业务功能也就是说我越权支付,然后用户密码找回验证码等等。好 API 接口了。那主要就是我们要把地址先拿到,拿到地址之后其实其他的测试方法都差不多。那么拿了这个地址了,我们有两种方式。第一种说从开发团队那边直接拿到一份 API 该的一个列表,然后并且搞得清楚他们每一个参数是做什么用的。然后对这些接口了去进行一些扫描测试以及一些逻辑性测试,和传统概念区别不大了。
第二个就是有可能我们从开发团队那边拿了不全对吧。所以我们可以用 reay 去打开一个端口,会使用这个 burp suite 打开一个端口。然后手机设置一下代理,把我们的数据包了从那边经过一下,然后把这个地址也收集一批。第三个就是比较麻烦了,刚才也讲到了,没办法轻易了解这个数据包的格式,然后比较麻烦。而且不好轻易去各做数据包,你必须通过程序的方式,你人工的是很没有办法去改数据的。但有些数据它就是这个十六进制的。那他不是说就是你看到的铭文说了你得模拟一个客户端,用于封装数据包。看你的这个人手够不够,人手不够的话,测了也没有太大意义。
每次我们检测出漏洞,或者遇到应急响应事件可以将它录入到我们的安全系统中,方便我们积累经验
那这里有个图,就我们这个团队了,对公司的一些漏洞了一些整体情况。那有季度的一个报表,有这个部门的一个报表以及统计信息,就是它的漏洞类别的一些统计信息。
那今天主要就分享这四点内容,从这个培训到这样一个钩子的制作以及这个代码审计,那最后以及安全测试。那么这一次的话题就讲到这里了,那希望对大家有所帮助。再见。
作者:汤青松
日期: 2022年3月15日