037 代码安全扫描【产品】

源代码是构成信息系统的基石。对源代码进行安全审计,可以在系统实现阶段就发现大量的安全漏洞和潜在威胁隐患,可节省5%-20%的后期安全维护费用,减少10%-50%的系统安全漏洞。 

从技术上分析,大中型政企机构的信息系统,往往规模庞大,涉及大量定制开发、外包开发、开源软件集成、第三方组件使用等,并且开发人员水平参差不齐、开发管理任务复杂、开发语言种类繁多,对信息系统的安全性带来较多挑战。通过源码安全审计,可以及早发现各类安全漏洞和威胁隐患,节省大量安全建设成本及软件运维成本。

《中华人民共和国网络安全法》正式颁布实施以来,国家对包括政企机构的网络安全监管日益完善,不管是网络安全等级保护制度还是行业监管制度,都对信息系统开发管理、源码安全审 计做出了规范要求,源码安全审计作为信息系统安全建设的重要组成部分,日益受到大中型政企机构的重视,在安全建设过程中, 对源码进行安全审计的机构比例不断提高。 

静态分析:不运行代码的方式下,验证代码是否满足规范性,安全性,可靠性,可维护性等指标的一种代码分析技术。

实现原理:词法分析,语法分析,控制流,数据流分析等对程序代码进行扫描,使用数学方法进行分析。

1 技术概述

1.1 基于污点传播分析的自动化挖掘技术

1.1.1 检测模型

自动化代码安全检测是一种自动化挖掘、发现源代码漏洞的安全检测。在人工形式下的代码审计中,通常关注以下三点:

(1)输入源

即用户输入可进入代码逻辑的入口,如PHP语言中的GPC,JAVA语言中的HttpServletRequest等等。此外,在对框架开发的工程进行代码审计的时候,我们还会关注框架自身封装的一些入口方式,比如SpingMVC中的@RequestParam注解。

(2)净化操作

净化操作包括两个部分:系统自带的净化操作函数以及用户自定义的净化操作函数。

这些净化操作函数通常可以针对某种漏洞类型将传入的参数进行一定程度的字符串变化,从而消除某种漏洞的威胁。比如htmlspecialchars函数,被广泛认为是用于消除XSS漏洞影响的,虽然在某些场景下一样可以被绕过。

(3)危险函数

危险函数是触发漏洞的位置,一般按照漏洞类型进行分类别讨论。比如SQL注入漏洞的PHP危险函数为mysql_query,调用该方法执行查询,如果一旦危险参数可控,则会造成SQL注入漏洞。

目前市面上有基于正则表达式和基于语义分析的两种检测方式,基于正则表达式的传统代码安全扫描方案的缺陷在于其无法很好的“理解”代码的语义,而是仅仅把代码文件当作纯字符串处理。目前较为成功的静态扫描商用产品都运用了语义分析、语法分析等程序分析技术,如开源的Rips中,使用的是PHP内置的Tokenizer系列API来获取token流。在token流的基础上进行分析,可以通过每个节点的解析器代号对代码进行一定程度的理解,如T_ABSTRACT表示抽象类、T_AND_EQUAL代表赋值运算符。

除了在token流的基础上执行代码分析,市面上越来越多的代码漏洞扫描器选择了从抽象语法树层面入手执行代码分析,相比冗长的token流,语法树层面的分析显得层次分明、容易理解,这对于平台开发人员来说是个好消息。

通过更加高级的语义、语法分析,我们可以从待分析的代码中获取更多的信息,通过这些信息,就能够大大提高漏洞检测的准确度,这也是为什么目前绝大多数商用软件都选择在语义、语法分析的基础上执行代码安全检测。

1.1.2 系统层次

一个基于程序静态分析的自动化审计系统大致应该有以下几个部分:

(1)静态分析层

静态分析层负责对代码文件进行“理解”,完成语义、语法层面的分析,比如生成抽象语法树、生成程序的控制流图等基础的分析结构。同时,针对PHP语言的一些变量类型进行分类抽象,用于后续的数据流分析中。

(2)数据流分析层

数据流分析主要负责收集代码中的变量传递流向,同时收集变量的净化情况。数据流分析是污点传播分析的基础,其收集的信息越全面越准确,后续的污点传播分析才会准确。

(3)污点分析层

污点传播分析是依照数据流分析过程获取的一系列程序信息来进行漏洞判定的模块。

(4)其他分析层

其他分析包含针对PHP多个代码文件的联合检测、结合程序的上下文信息进行更加细化的漏洞判定分析、过程内和过程间分析等等。

1.1.2.1 语义语法分析

抽象语法树(Abstract syntaxtree, AST)是将源代码按照一定的语法结构,并且将一些冗余的细节抽象为树状结构进行表示。市面上有很多语义语法分析工具,如ANTLR,Yacc,Lex,PHP-Parser等等。

这里着重介绍一下PHP-Parser(https://github.com/nikic/PHP-Parser),作为PHP语言的语法分析工具,其本身就是使用PHP语言进行实现的,且支持节点遍历等功能。

1.1.2.2 控制流程分析

仅仅在抽象语法树的基础上进行分析是不够的,因为抽象语法树无法很好的获取程序中流程控制语句的信息。因此需要在抽象语法树的基础上构建控制流程图,将PHP中的分支跳转进行整理,以基本块的形式编排抽象语法树的节点。

在程序分析的时候,必须要对每个分支进行独立的分析。生成新基本块的依据是一些控制流程语句,这些语法结构都是固定,大致有以下几种:

(1)条件分支

If语句、Switch语句、Try catch块、三目运算符、Or语句

(2)循环结构

For语句、Foreach语句、While语句、Do…While语句

(3)终止结构

Break、Continue、Throw

(4)返回结构

Return

生成控制流图的方法是当遇到条件分支和循环结构时,需要生成一个新的基本块,将这些代码块中的语法树节点插入进去;当遇到终止结构时,停止当前基本块的生成;当遇到返回语句时,停止图的构建。

1.1.2.3 抽象内置函数

在任何代码中,都会有PHP内置函数的调用,如果不对这些内置函数进行处理,或者说自动化分析不“理解”这些函数的含义和效果,可能会造成大量的误报,由于PHP内置函数众多,每个函数都人工进行处理是不现实的,最好的方式是对这些内置函数进行分类。按照功能和对程序分析的影响,大致可分为:

(1)返回常量值

比如strlen()或者md5(),一旦参数进入这些内置函数将会返回常量类型的结果,比如返回纯数字、布尔值、纯字符串,这种处理需要考虑为针对参数的一种净化方法。

(2)返回参数的一部分

某些内置函数如trim() 或者 array_keys() 会将参数的一部分或者全部进行返回。针对返回值,该分类可以分为返回array、返回array的单个元素、返回string类型。

(3)净化操作

针对某种漏洞类型进行参数净化操作的内置函数,比较典型的有addslashes,htmlspecialchars等函数。

(4)字符串切割变换

比如内置函数substr()或者chunk_split(),这些内置函数会返回传入参数的一个子串。由于这种操作会破坏参数原有的结构,如果不追求绝对精确分析的话,实际上完全可以将这类函数的调用视为某种程度的净化处理。

(5)编码和解码函数

编码函数

如urlencode()或者base64_encode()等执行某种编码操作的内置函数。

解码函数

如urldecode()或者base64_decode()等执行某种解码操作的内置函数。注意,如果在分析中遇到了解码函数,通常认为该函数调用前参数所受到的一切净化都是无效的,如果直接进入危险函数就会触发漏洞。

(6)回调函数

一些内置函数会执行参数指定的其他函数,比如array_walk(),array_map(), set_error_handler()。如果标识其他函数名的参数可以直接获取到,那么将该调用信息进行进一步分析,

①获取文件句柄

一些文件操作类型的危险函数的文件参数是以resource传递进来的,因此如果仅仅去判断是否受污染是不可行的,所以需要对获取文件句柄的函数进行分类。

②白名单机制

在条件分支中遇到这些函数如in_array,array_key_exists等函数,可以默认是一种基于白名单的净化处理。

③正则表达式校验

比如preg_match和ereg函数,这里不可能去分析具体的正则表达式,因此为了减少误报,可以认为调用这些函数进行校验都是有效的过滤。当然如果选择牺牲误报而减少漏报,也可以认为这些函数的调用是不起作用的,可以直接报警。

④其他函数

其他不属于上述类别但是需要额外分析的内置函数。

1.1.2.4 数据流分析

数据流分析层所做的工作是收集程序中的变量值传递、特殊函数调用等信息。可以理解为数据流分析就是一个综合的程序信息搜集,目的是为了给后续的污点分析提供详细的参考信息。

对于数据流向的识别和搜集,应该考虑到以下两种情况:

(1)针对代码中的赋值语句

(2)PHP内置函数导致的隐藏数据流,比如调用list、func_get_args等函数。

1.1.2.5 污点传播分析

污点分析过程是在程序分析中,一旦发现危险函数的调用则启动分析。对传入危险函数的危险参数进行分析,结合数据流分析时该危险参数的一些程序信息,如净化信息、内置函数处理信息等进行判断,如果一旦发现该变量可以被用户控制并且没有进行有效过滤,则判定为漏洞。

①定义危险函数

首先考虑如何定义危险函数。在人工代码审计的时候,会按照漏洞的类型着重关注某一批函数,比如mysql_query, file_get_contents, eval等等。然后我们会找到这些函数的某些参数,然后判断这些参数是否经过程序处理后还是会存在漏洞。因此,污点分析时,只需要关注危险函数的名称和危险参数的位置即可,配置示例如下:

print  =>  array(array(1),$F_SECURING_XSS)

该配置的含义是:print函数是一个可能引发XSS漏洞的危险函数,并且其参数1是一个危险参数。

②定义净化操作

定义一个参数是否受到了有效净化是污点分析中比较重要的环节,这关系到后续漏洞判定的准确性。根据人工代码审计的经验,可以抽象总结出一个PHP实现的净化操作可以有以下方式:

(1)使用PHP内置的或者用户自定义的净化函数执行净化,比如调用addslashes等函数

(2)使用PHP内置的一些校验类型的函数,比如类型判断、正则表达式校验、字符串切割、回调函数以及编码解码等操作。这些操作都会进行一定程度的净化,如果在实践中,期望有较高的精确度,则可以认为凡是调用这些函数,就认为是有效的净化;如果我们期望降低漏报率,则可以忽视这些内置函数影响或者执行更加细致的分析。

(3)使用逻辑判断进行校验,如使用了==或者===与某些静态常量进行了比较操作,则认定为该变量接受了净化。

③执行污点分析

有了上述基础,可以很清晰地执行污点分析判断漏洞了,大致的过程如下:

(1)在执行数据流分析过程中,如果发现了敏感函数的调用,则启动污点分析。

(2)查询危险函数配置列表,获取到需要判断的危险参数列表。

(3)向上找到连接的基本块信息,关注一个基本块内所有的数据流记录,找到数据流记录右边的值,提取出该变量。

(4)如果该变量进入到了内置函数,则按照之前章节中整理的内置函数的作用判断是否受到了有效的净化。

(5)当遍历时找不到基本块中相关的赋值语句,或者赋值的值为字符串、数字或者布尔值,则停止污点分析。

此外,需要格外注意编码和解码的影响,:

(1)一个回溯变量被进行解码操作后(base64/hex/zlib…),该变量向上的所有净化操作都可以认为是无效的。

(2)如果一个变量被编码和解码多次,则进行抵消分析操作,并由抵消之后的结果进行判断。

1.1.3 其他分析层

在整个语法树遍历、控制流图生成的过程中,可能还会伴随着以下几种情况的细致分析:

(1)过程内、过程间分析

当分析代码时,会遇到方法内部的变量传递与程序信息搜集;或者会遇到一个变量在多个方法之间通过参数进行传递。支持过程内、过程间分析的重点是作用域之间的对应和转换。此外,当我们遇到某个函数中执行了对某个参数的过滤,可以将其加入到净化函数列表。

(2)多文件分析

执行多文件分析时,很多系统仅仅只是依赖use、include、require等关键词来寻找当前文件所包含的其他文件,但是很多程序实现中,往往会用到autoload技术,或者在某个统一入口执行文件包含的操作。所以我们有很大的几率会遇到某个文件中调用了一个其他文件的方法,但是在该文件中却找不到该方法的定义语法结构的情景。

因此多文件分析时,我们最好在分析初始化阶段中获取到所有类定义、方法定义、通用函数定义以及其所在的路径位置,将这些信息保存在内存中。当我们后续分析时,可以直接操作该内存结构,动态提取需要的方法定义。

2 功能概述

①非本地工具,而是一个平台

在大型公司,可能有成千上万的产品线,涉及到的代码库数量庞大并且编程语言繁杂,面对这种状况,本地化工具过于松散,不好管理,接入效率低的缺点,而统一的扫描平台可以提供一整套的自动化接入方案。统一的源码安全扫描平台可以区分多场景的任务类型,提供多种接入方式,可以更高效、自动化地提供安全扫描能力。

②软件开发关键环节,中流砥柱

SDL(security development lifecycle) 是微软提出的从安全角度指导软件开发过程的管理模式,源代码安全扫描是SDL方法中的重要环节。从软件的开发流程上看,源代码安全扫描处于软件开发流程上下游,在软件上线之前需要通过安全扫描。在实践中,源代码安全扫描是以插件的形式,嵌入到软件研发的流程中,为业务线代码保驾护航。这将要求平台必须具有高可用性,高响应及时度和完善的用户反馈机制。 

③战略地位,支撑安全红线

企业有一条安全红线,业务必须遵守的最基本安全要求。源代码安全扫描也可以用于在系统上线前发现漏洞与违规的内容,禁止漏洞带到线上,一定程度保护企业业务安全。

2.1 整体架构

从分层的角度,源码安全扫描平台大致分为五层,每一层都有明确的职责划分。

2.1.1 接口层

接口层是平台对外输出能力的入口,主要对接口请求进行合理性校验,权限校验,参数校验,以确保请求是合法的,非恶意的。功能上主要分扫描接口和查询接口,扫描接口用来接收发起扫描任务,查询接口用来查询扫描任务的结果数据。

2.1.2 任务管理层

每一次扫描就是一个任务,任务管理层是整个平台的中心部分,管理所有任务的扫描状态。主要包括三个职责:

跟踪。任务管理层跟踪和记录所有任务的扫描状态。

策略下发。源码扫描平台根据业务的实际场景,针对不同任务实施不同的扫描策略,比如根据代码的变更情况使用缓存策略,根据代码库的特性调整超时时间等。

超时监控。每个任务都有一个超时时间,当任务扫描时间超过期望值,任务会结束并返回超时异常。

2.1.3 引擎管理层

引擎管理层是对引擎调用的一层封装,在企业里面涉及到多种计算机语言,不同语言可能使用不同的源码分析引擎。引擎管理层向上抽象出扫描接口和报告接口,扫描接口接收扫描数据,下层实现具体的引擎调用策略,报告接口获取不同引擎的扫描结果,统一生成扫描分析报告数据。

2.1.4 源代码管理层

源代码管理层主要关注目标源码获取、源码存储和源码安全。

 源码获取。源码获取主要支持两种方式,第一是用户上传源码的方式,这种方式需要用户自己把源码打包,上传到web服务器。第二种是和企业级代码托管平台打通,源码管理层根据托管平台提供的安全协议拉取源码。

 源码存储。源码存储主要包括两个方面,其一是需要扫描的源码,这部分源码存储时间不长,在扫描结束后的一段时间内会被清除;其二是有漏洞的源码,这部分源码会被永久存储,用来后续分析漏洞报告使用。

 源码安全。源码安全扫描涉及到业务线的源码下载与分析,对于企业来说源码具有最高保密性,需要防止源码泄露的问题发生。这里我们有两个层面措施:第一是服务器隔离,所有对源码操作和源码存储都放在指定的服务器上,并且由源码托管平台负责,这种物理隔离大大减少平台自身的管理成本。第二是所有源码存储传输都采用了加密,这里采用常用的方式,使用AES加密源码,RSA加密AES的key。

2.1.5 扫描引擎

引擎主要利用静态代码分析技术分析源码中可能存在的漏洞。

2.2 SDL中的实战

研发工程师(RD)提交代码,进行源码安全扫描,编译,持续集成到部署上线,这一系列步骤是全自动化的过程,而源码安全扫描嵌入到整个流程中,必定牵扯到各个平台。

2.2.1 接口可用性

一般平台的接口可用性要求是四个9,即SLA为99.99%,即使企业内部使用,也要达到99.9%。SLA指标很重要,因为一旦某段时间内接口不可用导致软件持续集成阻塞,会影响到正常的产品发布的流程,导致产品上线出问题。比如企业内某个产品紧急修复了bug需要上线,但是却在安全扫描这一步卡住,产品线的同学可能会抓狂了。

为了保障接口的可用性,以及及时发现故障,可做以下策略:

①慢启动。接口的服务逻辑应该尽可能少的计算和IO访问,这里将请求的原始数据做一次redis写入,然后就返回,其他的任务扫描逻辑之后才被后续的逻辑“慢”启动起来。

②接口监控。监控每个接口每次接收请求到做出响应的时间,在响应时间超出一定范围(一般与调用平台约定),则进行邮件告警。

③降级策略。在遇到故障无法在短时间内恢复的情况,启动降级策略,舍弃一定功能的情况下,保障接口能够及时返回,防止流程卡住。

2.2.2 “慢”是永恒话题

源码安全扫描作为软件集成发布的一个步骤,一定程度上影响软件发布的时间。如果安全扫描耗时过长,将会大大拖累的整个上线流程,安全部本身也会承受着很大的压力。

2.2.2.1 此增量非彼增量

或许有人会讲,RD大多数时候可能只会修改几个代码库的文件,这样怎么会有扫描速度慢的问题呢?这样的想法是基于两个前提:第一,扫描是单文件;第二,文件与文件之间是没有关联的。但事实上,前面漏洞分析技术也有提及,白盒代码扫描过程其实会对相关联的多个文件进行扫描,可能修改了一个文件,但是有很多其他的文件引用了这个文件,为了保险起见,所以会把相关联的文件一并执行扫描。

所以一开始的做法是:不管代码库如何变化,都使用全量扫描,而这将导致扫描速度特别“慢”,经常会收到业务线同学的反馈,对运营造成很大的压力。

2.2.2.2 增量扫描的实践方法

为了提高扫描的性能,尝试做了增量扫描策略。

大致思路是,如果我们能够对本次变更的代码进行分析,找出当前代码库中变更文件引用的文件和被引用的文件,将这些文件进行扫描,最后跟全量合并结果。这样可以大大减少扫描的文件数,提高扫描速度。

具体做法是,在源码管理层嵌入增量策略层,利用简单的源码分析技术,得出文件的引用流向图。通过对图的遍历获取到增量涉及到的文件。

2.2.2.3 其他优化策略

除了增量扫描,我们还有如下优化策略:

①无变更文件使用历史结果。相同代码库前后两次扫描,如果文件没有任何修改,则认为两次扫描结果一致。

②基于无关文件变更的缓存策略。一个代码库,前后两次扫描,如果前后两次变更的文件与安全扫描无关,则复用第一次扫描结果。

③闲时扫描。很多时候扫描慢也可能是资源紧缺有关,一个平台的访问量也可能集中在某个时间段,因此,闲时扫描作为一种缓存策略,可以大大提高扫描的速度。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容