IAST技术进阶:关键语言支持
星期二, 八月 17, 2021
新一代IAST灰盒安全测试技术凭借其极高的检出精度、深度的业务透视能力及高度自动化CI/CD支持能力,正快速由行业头部用户的场景化探索实践向广大中小型用户规模化应用实践演进。由于其特殊的技术原理,使得该项新技术对业务语言的覆盖支持成为关键落地因素之一。
今年6月,中国信息通信研究院(以下简称“中国信通院”)正式发布《交互式应用程序安全测试工具能力要求》行业标准(以下简称“IAST标准”),并于首届DevSecOps敏捷安全大会(DSO 2021)首次公开标准解读。目前,中国信通院已完成首批评估测试,悬镜灵脉IAST是全球首个通过该项专业测评认证的IAST工具。
自IAST标准发布起,中国信通院同步启动了首批IAST工具的测试工作。依托标准要求中的34个功能指标项,在统一并发场景下进行性能检测记录。通过对测试结果分析发现,各厂商IAST工具在以下三方面存在较大差异:
1、对主流编程语言支持的覆盖率;
2、对不同安全漏洞类型及结果的定义规范;
3、配置缺陷和弱密码等低危漏洞或信息漏洞的检测结果。
其中,首当其冲的是对主流编程语言的覆盖率问题。在IAST标准当中,“支持的编程语言”被标定为全标准的第一条测试要求,由此可见其重要性。IAST工具是否支持多种主流编程语言,直接决定了其是否能适配客户各种各样的实际开发场景,进而满足助力客户业务发展的需求。
图:IAST标准中对编程语言的要求
多种编程语言的支持,对用户的意义不是够不够好,而是能不能用。如果用户核心业务使用的语言没有被IAST探针支持,即使IAST产品有100分的其它功能,对用户来说也是0分。
01 IAST工具介绍
IAST(Interactive Application Security Testing, 交互式应用安全测试)是应用程序在进行自动化测试、人工测试等任何与应用程序进行“交互”的同时,能自动分析应用程序安全风险的技术。它可以实时返回结果,因此不会额外增加CI/CD的时间。
相比于其他AST技术,IAST只会分析“交互”产生时所影响到相关代码的安全风险,而不是扫描所有代码、配置文件或遍历整个站点。IAST更适合在QA环节使用,让安全团队在相对不影响开发、测试现有流程的情况下,较早地发现应用程序中存在的安全风险。
需要注意的是,与其他AST技术相比,IAST还是一个比较新的概念,其基于请求、代码数据流/控制流来综合分析应用程序的安全风险。IAST运行时插桩技术可以发现更多应用程序本身的安全弱点,以及应用程序中第三方组件的公开漏洞。由于该技术针对不同语言需要研发不同的插桩探针,因此对于厂商而来,其IAST工具每增加对一种语言的支持,即相当于开发一款新产品,也就意味着IAST工具支持的编程语言越多,厂商所需投入的人力成本和经济成本也就越大。
支持多种编程语言插桩,是IAST技术未来发展的必然趋势,原因在于不同行业由于属性不同,对开发语言有特殊要求。例如金融行业,普遍以Java语言为主;互联网行业则是多语言环境的典型代表,使用的语言五花八门,Java占比较高,同时也会使用Python、Go、Node.js、.Net等语言来适配多种业务场景。如此,一款好的IAST工具支持多语言插桩是必要基础,也是IAST工具厂商在技术上必然的拓展方向。
02 主流编程语言分析
截至2021年7月TIOBE发布的编程语言指数报告,排在前十名的编程语言中,常见的应用开发语言主要有Java、Python、C#、JavaScript、PHP这5种(VB.Net 随着 .Net Core 和 .Net 5.0 的推出,跨平台方面优势将不再明显,因此不再讨论)。
图:TIOBE-2021年7月编程语言排行(TIOBE Index for July 2021)
根据TIOBE指数排行,这里列出了百度指数、Google Trends以及Stack Overflow Trends近年内对Java、PHP、Node.js、Python、C#这些语言的热度检测趋势:
图:近4年间Java、PHP、Node.js、Python、C#编程语言百度指数趋势
图:近4年间Java、PHP、Node.js、Python、C#编程语言Google Trends趋势
图:近12年间Java、PHP、Node.js、Python、C#编程语言Stack Overflow Trends趋势
通过对比上述各平台的趋势发现,对着数据科学、人工智能等领域的兴起,Python的热度扶摇直上。作为一种动态类型语言,使用Python作为Web应用程序的主要开发语言可以在初期为项目节省大量的时间和成本。例如:早期的YouTube、知乎平台等,均使用Python作为主要的Web开发语言。
但是由于Python在多线程上的硬伤和动态类型语言后期不易维护的特点,很多企业在完成初期的用户积累后,都会选择使用一门静态类型的语言来重构他们的应用,例如Java、C#或者Golang。
Java凭借其简单易懂的语言、活跃的社区支持、以及JVM优秀的跨平台支持,使其成为全球使用最多、最广泛的Web应用程序编程语言。
C#背靠Microsoft,凭借ASP.NET、Razor、良好的社区、宇宙级IDE:Visual Studio,再加上语法和Java、C等语言相近的特点,也在Web应用程序编程语言中占有一席之地,但在国内热度相对较低。
PHP与Python 一样,也是一种动态类型语言。由于一定的历史原因,PHP作为“世界上最好的语言”在国内热度一直较高。低学习门槛、易于开发和部署的特点,让很多企业选择使用PHP来编写公司官方网站或者与微信开放平台对接。
Node.js在严格意义上来说并非一门语言,而是使用JavaScript引擎构建的平台。Java、C#契合Web应用程序开发的一部分原因是其能够和JavaScript/TypeScript友好地融合(例如:Jsp技术和Razor技术)而Node.js作为直接使用JavaScript来构建的平台,在这方面有天然优势。同时,由于JavaScript引擎的更迭,其效率越来越高,加上原生支持异步处理等特点,非常适合快速构建Web应用,因此热度也在不断上涨。
目前国内,面向互联网的Web应用程序主要以Java、PHP来编写,内网应用则呈现出多元化的态势,以快速实现功能为主要目标,因此使用Python、Node.js、Golang、C# 来开发的应用占比也相对更高。
03 主流语言插桩原理简述
IAST插桩探针想要分析数据流需要做两件事情。第一点是如何“理解”代码。我们需要根据不同语言来区分,需要关注哪些函数,这些函数里哪些是污点输入?哪些是污点传播?哪些是污点清洗?以及又有哪些是污点汇聚?这个我们可以根据不同的漏洞原理来做分析,第二点,我们如何捕获关键函数的调用情况?这需要我们从各种语言底层做突破,使得我们能读取到关键函数何时被调用、传入了什么参数、数据传递给了谁。下面将针对不同语言,结合悬镜实际产品研发经验,浅析IAST插桩捕获关键函数的原理。
Java
如果语言本身提供了插桩的接口,那么探针开发的难度将会大大降低。例如Java提供了一个instrumentation 接口。通过该接口,可以以一种标准的方式,在启动应用时添加javaagent参数来加载插桩探针,从而实现动态数据流污点追踪。大致流程如下:
图:Java实现动态数据流污点追踪流程
如图所示,通过红色的JVM Agent路径,就可以获取到Hello类中的方法何时被调用、接受到了哪些参数等信息。更进一步,就可以阻断它的执行(RASP的功能),甚至修改它的执行逻辑。当然,即便官方已经提供了标准做法让插桩探针的研究少走弯路,但是构建成熟的Java探针并非易事。
除Java外,其他一些语言也使用了JVM虚拟机,例如Scala、Kotlin等,这些语言的插桩原理比较类似,不再赘述。
PHP
PHP Core使用C语言编写,默认情况下,PHP开发者使用的所有函数都在PHP Core和C中定义。当PHP程序在运行时,有三个主要组件进行交互:
PHP程序通常有如下两种主要运行方式:
在PHP中,没有多线程的概念,每一个请求都会生成一个新的进程。因此,很多PHP服务器上同时会处理成百上千个PHP进程。一个进程可以理解为一条请求。当一条请求进行PHP应用程序后,将会经历如下生命周期:
1、 MINIT(模块初始化)
2、 RINIT(请求初始化)
3、 执行响应PHP代码逻辑,生成相应并返回
4、 RSHUTDOWN(请求关闭)
5、 MSHUTDOWN(模块关闭)
PHP插桩检测的主要方式是替换内部函数,将原始请求暂存在特定的位置,并触发探针分析,分析引擎通过比对函数列表检索出被调用的函数并记录,然后释放原始请求,再以同样的方式继续执行,分析之后的内容。流程如下:
图:PHP插桩检测流程
如上图所示,PHP插桩探针包装了原始函数,就像安检一样,你和你的行李(原始数据)需要通过安检仪器后才能进入下一阶段。
Node.js
JavaScript的灵活性为Node.js动态插桩检测提供了便利。但是由于Node.js有着众多版本,很多版本之间API变化很大,因此,对于不同Node.js版本插桩探针的构建将会有不少的坑要踩。对于Node.js插桩探针,通常采用和 PHP 类似的方法 ——包装函数。要构建一个Node.js插桩探针,至少需要从以下几个方面着手研究:
首先,由于Node.js导入模块时,会将模块保存在本地位置(如node_modules)。通过require语句或者遍历本地模块目录,可以检索出所有需要跟踪/监听的模块。然后,根据上一步得到的模块列表,针对性地筛选出需要包装的方法/函数。接着,将构建的Hooks置于包装后的方法中。最后,当Hooks被触发时,调用引擎对其进行分析。至此,分析引擎将能获取到原始数据,根据需要,我们可以对数据流进行阻断、处理、放行等操作。
与PHP不同的是,在Node.js中导入模块是一个同步的操作,添加一个跟踪器来跟踪模块导入的开销可以忽略不记。触发Hooks会影响代码逻辑的执行时间,但这是不可避免的,和PHP一样,需要筛选出关键函数来进行监控。
Python
得益于Python内建的自省机制,使得在Python上构建插桩探针变得安全可靠。和PHP、Node.js类似,构建Python插桩探针的主要方法同样是包装底层函数。PEP 302提供了import hook的方式,在被引用的模块被加载前,就可以动态地对特定函数添加装饰器,也避免了在其他位置重复引用而导致装饰器失效的糟心问题。
虽然对于使用Python定义的函数进行包装相对容易,但在常用的CPython解释中,很多类都是由C定义(例如,大多数的DB Driver)。好在绝大多数的DB Driver(SQLite、MySQL等)都遵循PEP 249 ,这样就有机会顺着DB Driver的调用链一路打补丁。对于未遵循DBApi2的DB Driver(NoSQL等),则需要更多的工作量。
.Net
众所周知,.Net技术体系与Java高度重叠。表现在探针产品层面,就是.Net探针与Java探针在作用原理与产品架构上高度相似。所不同的是,.Net CLR并未提供类似java.lang.instrument包那样易于使用的接口。实际上,.Net探针的主要技术难度集中在如何像Java改写bytecode一样动态地改写JIT阶段的CIL编码。
在实际实现时,.Net探针利用了CLR的Profiling接口,通过C++实现一套类似JVM Instrumentation API的IL改写机构,以将基于C#安全保障业务逻辑注入到关键方法之中。除去这套IL改写机构带来的麻烦外,GAC、mscorlib/System.Private.CoreLib等 .Net特性都最终使得探针在形态上与前述各语言区别较大。
目前,一场变革正在.Net技术世界中发生,无论是转向开源的.Net Core还是作为“下一代”的.Net5/6都在底层上引入了不少变动。幸运的是,在CLR层次上的变动仍然相对保守。因此,目前探针同时具备对传统的 .Net Framework、.Net Core 与最新的 .Net 5/6 环境的适应性。
GO
Go 语言是近几年兴起的技术门类,其核心优势在于开发/运行效率高、工具链完善且二进制易于部署。然而,对这种直接编译为二进制的技术体系,利用 Runtime 机制进行插桩的方式就不再有效。因而,Go 探针在形态上与使用体验上与其他探针云泥之别。得益于Go的开源特性,可以直接对Go工具链的源码进行改写。我们通过将 `go build`命令逻辑进行改写,实现`xmirror-go build`命令,即可在项目构建过程中,将安全保障逻辑加入关键方法之中。
悬镜AI(北京安普诺信息技术有限公司)由北京大学白帽黑客团队"Xmirror"主导创立。公司力求以人工智能技术赋能信息安全,在公司研发的产品中,深度结合机器学习、红蓝对抗、攻击链模型等技术,为企事业单位提供前沿高效的DevSecOps全流程AI安全管家服务。