代码审计指南

目录

1.     简介... 3

1.1      关于代码安全审计... 3

1.2      代码安全审计流程... 4

2.     代码安全审计方法... 5

2.1      代码安全审计准备... 5

2.2      代码审计发现和收集信息... 7

2.3      静态代码分析... 8

3.     修订记录... 14

  1. 简介
    1. 关于代码安全审计

代码安全审计是查找代码中安全漏洞的方法。在“安全左移”的发展趋势下,代码审计逐渐成为确保代码质量的一个关键环节。代码安全审计通常可以分为:自动化审计和人工审计。

自动化代码安全审计是以自动化工具的方式查找代码的安全漏洞,这样的工具一般称为静态代码检测工具(SAST)。SAST的一大优势是能够极大地减少查找代码漏洞的时间。

然而,这类工具往往最让人印象深刻的首先就是“误报率”,据统计,SAST类的产品在OWASP基准测试中最高检出率达到85%,但误报率也高达52%。高误报率的主要原因在于程序无法准确理解开发人员的代码含义。其次,自动化代码安全审计往往无法查找业务相关的漏洞,比如支付漏洞、任意密码重置,优惠券叠加等。

相比于自动化,人工审计优势在于查找业务逻辑相关漏洞,但对应成本也远高于自动化工作,主要体现在两方面:

① 学习成本

审计业务逻辑漏洞需要做到“三懂”:

a) 懂代码。不同系统使用的代码和开发平台是不同的,Java、C#和PHP甚至是C++,各自关注的安全点也各不相同。而且很多安全问题在框架层次就已经解决掉了,典型的认证问题可用Shiro解决。如果看不懂框架,那么连实现的地方都无法找到,更不用说找出问题了。

b) 懂业务。查找业务问题需要对系统业务有深入的了解,除了密码重置等通用型业务,还包括不同行业的独有业务,如电商的支付和优惠券、金融的转账对账等。

c) 懂安全。在了解代码和业务的基础上,还需要从攻击者视角审查业务安全漏洞,才能进一步发现业务中可能存在的安全问题。比如典型的任意密码重置漏洞、优惠券叠加漏洞,都需要审计人员理解漏洞原理和实现方法,才能“对症下药”查找业务逻辑的不足。

② 时间成本

人工代码安全审计的时长取决于代码量的多少。随着代码和业务复杂度的提升,完整实施代码安全审计所需的时间成本也同步增加。

因此自动化和人工审计两种方法并不是相互对立的,而是互为补充,二者并用才是业界代码安全审计的最佳实践。

    1. 代码安全审计流程

从上面的审计流程可以看到,代码安全审计工作的关键环节在于:

1. 设定审计基线,包括三方面:

a) 针对代码和开发平台的基线。这里需要考虑的是代码开发语言、架构、安全审计质量准则等。不同代码和平台之间关注的问题也不同。

b) 合规基线。很多组织需满足合规要求,一些合规条例包括:等级保护要求、支付卡行业标准、中央银行规定以及PCI-DSS(第三方支付行业数据安全标准)等。

c) 针对漏洞的基线,即需要明确代码安全审计要覆盖哪些漏洞,仅仅覆盖红线、OWASP Top 10的漏洞还是要覆盖所有类型的漏洞?

2. 人工审计。主要分为两部分工作:

a) 验证工具扫描出的问题。

b) 查找工具未覆盖的安全问题。

  1. 代码安全审计方法
    1. 代码安全审计准备

在理想的情况下,审计者会参与到应用程序的设计阶段,但这种情况很少发生。然而,不管代码变更的规模有多大,启动代码审计的工程师应该指导审计人员查看任何相关的架构或设计文档。最简单的方法是在初始电子邮件或代码审计工具中包含一个指向文档的链接 (比如,它们存储在一个在线文档库中)。然后,审计人员可以验证安全控制措施是否正确处理了关键风险,以及这些控制措施是否用在了正确的地方。

为了有效地进行审计,审计人员应熟悉以下方面:

  1. 应用程序功能和业务规则

审计人员应了解应用程序当前提供的所有功能,并获取与这些功能相关的所有业务限制 、规则。还有一种情况是,要注意潜在的计划中的功能,这些功能可能会出现在应用程序的路线图上,从而在当前的代码审计过程中对安全决策进行提前验证。这个系统失败的后果是什么?如果应用程序不能按预期执行其功能,企业会受到很大影响吗?

  1. 上下文

所有的安全都在我们试图保护的范围内。在苹果的应用程序上推荐军事安全标准机制将是矫枉过正、不合适的。什么类型的数据会被操纵或处理?如果这些数据被泄露会对公司造成什么损害?上下文对于安全代码审计和风险评估十分重要。

  1. 敏感数据

审计人员还应记录对应用程序敏感的数据实体,如账号和密码。根据敏感度对数据实体进行分类将有助于审计者确定应用程序中任何类型的数据丢失的影响。

  1. 用户角色和访问权限

了解被允许访问应用程序的用户类型很重要。是面向外部还是内部给“信任”的用户? 一般来说,只有组织内部用户才能访问的应用程序可能与互联网上任何人都能访问的面临不 同的威胁。因此,了解应用程序的用户及其部署的环境将允许审计者正确认识威胁代理。除此之外,还必须了解应用程序中存在的不同权限级别。这将有助于审计者列举适用于应用程序的不同安全违规/权限提升攻击。

  1. 应用类型

这是指了解应用程序是基于浏览器的应用程序、基于桌面的独立应用程序、网络服务、 移动应用程序还是混合应用程序。不同类型的应用程序面临不同类型的安全威胁,了解应用程序的类型将有助于审计者查找特定的安全缺陷,确定正确的威胁代理,并突出适合应用程序的必要控制。

  1. 代码

使用的语言,从安全角度看该语言的特点和问题。从安全性和性能的角度来看,程序员需要注意的问题和语言最佳实践。

  1. 设计

一般来说,如果使用MVC设计原则开发,网络应用程序有一个定义良好的代码布局。 应用程序可以有自己的定制设计,也可以使用一些著名的设计框架,如Struts/Spring 等。应用程序属性/配置参数存储在哪里?如何为任何功能/URL 识别业务类别?什么类型的类被执行来处理请求(例如:集中式控制器、命令类、视图页面等)?对于任何请求,视图是如何呈现给用户的?

  1. 公司标准和指南

许多公司会有由管理层规定的标准和指导方针。这就是管理层(最终负责组织的信息安全)如何控制对各种功能应用何种安全级别,以及如何应用这些安全级别。例如,如果公司有一个安全编码指南文档,审计者应该知道并理解这些指南,并在代码审计期间应用它们。

    1. 代码审计发现和收集信息

审计者为了保证信息有效性需要关于申请的某些信息。通常,这种信息可以通过研究设计文档、业务需求、功能规范、测试结果等获得。然而,在大多数现实的项目中,文档已经明显过时,并且几乎没有适当的安全信息(项目文档真的十分重要)。如果开发组织有架构和设计文档的程序和模板,审计者可以建议更新,以确保在这些阶段考虑(和记录)安全性。

如果审计者最初对应用程序不熟悉,最有效的入门方法之一是与开发人员和应用程序的首席架构师交谈,或者让开发团队分享一些关于关键安全事项的基本信息和控制。实际运行的应用程序的使用非常有助于让审计者很好地了解应用程序的工作方式。此外,对代码库的结构和使用的任何库的简要概述可以帮助审计者入门。如果关于应用程序的信息不能以任何其他方式获得,那么审计者将不得不花一些时间进行侦察,并通过审计代码来共享关于应用程序如何工作的信息。需要注意的是,该信息然后可以被记录以帮助将来的审计。

代码安全审计不仅仅是关于代码结构,数据也很重要。应用程序要处理的数据的上下文对于确定潜在风险也非常关键。如果应用程序是使用内置的、众所周知的设计框架开发的,那么大多数问题的答案都是预先定义好的。但是,如果是定制的,那么这些信息肯定会有助于审计过程,主要是捕获数据流和内部验证。了解应用程序的体系结构有助于理解应用程序可能面临的安全威胁。

设计是应用程序的蓝图,它为其发展奠定了基础。它说明了应用程序的布局,并确定了应用程序所需的不同应用程序组件。它是决定应用程序执行流程的结构。大多数应用程序设计都是基于 MVC 的概念。在这种设计中,不同的组件以有序的顺序相互作用,以满足任何用户的请求。设计审计应该是安全软件开发过程中不可或缺的一部分。设计审计也有助于以更好的方式实现安全需求。

收集建议设计所需的所有信息,包括流程图、序列图、类图和要求文件,以理解建议设 计的目标。主要针对数据流、不同应用程序组件交互和数据处理对设计进行了深入研究。这是通过手动分析和与设计或技术架构师团队的讨论来实现的。必须彻底了解应用程序的设计和体系结构,以分析可能导致应用程序安全漏洞的易受攻击区域。

了解设计后,下一阶段是分析设计面临的威胁。这包括从攻击者的角度观察设计,并发现其中的后门和不安全区域。威胁分析暂不在此文章详细说明,仅在下文简单陈述,读者可以搜索专门的威胁分析课程来进行学习。

    1. 静态代码分析

前文中介绍了自动化和人工审计两种方法应互为补充,二者并用才是业界代码安全审计的最佳实践。

在开展静态代码分析前,应该选取合适的工具,一般来讲,会有以下两个选项来识别错误:1.基于模式搜索的静态代码扫描脚本(内部和开源)。2.静态代码分析器(商业和开源)。当然现在也都可以结合使用,需要做一些详细的工具分析,了解工具特点,结合代码审计目标来进行合适的选型。

无论是自动化还是人工审计,所遵循的方法论是一致的。这里提出一种业界比较标准的方法:威胁建模。基于威胁建模的指导思想来进行静态代码审计往往是比较科学的,效率的,虽然威胁建模不是一种审计代码的方法,但它通过提供应用程序的上下文和风险分析来补充安全的代码审计过程。威胁建模的概念并不新鲜,但近年来,人们的思维方式发生了明显的变化。现代威胁建模从潜在攻击者的角度看待系统,而不是从防御者的角度。过去几年来,许多公司都大力提倡这一流程,包括微软,他们将威胁建模作为 S-SDLC的核心组件,他们声称这是近年来产品安全性提高的原因之一。

当源代码分析在软件开发生命周期之外执行时,例如在现有的应用程序上,威胁建模的结果有助于通过促进基于风险的方法来降低源代码分析的复杂性。审计者可以优先审计威胁建模被列为高风险威胁的组件的安全代码,而不是同等关注地审计所有源代码。

       威胁建模过程可以分解为3个高级步骤,对于代码审计来说,忽略了步骤3:确定对策和缓解措施,这一项可以由软件工程师们来进行软件上的修改和补充。

  1. 步骤1:分解应用程序。

威胁建模过程的第一步是了解应用程序及其与外部实体的交互方式。这包括创建用例来理解应用程序是如何使用的,识别入口点来查看潜在的攻击者可以在哪里与应用程序交互,识别资产,即,攻击者可能感兴趣的项目、领域,并确定代表应用程序将授予外部实体的访问权限的信任级别。这些信息记录在威胁模型文档中,也用于为应用程序生成数据流图。DFD 显示了通过系统的不同数据路径,突出了特权(信任)边界。

分解应用程序时要考虑的项目包括外部依赖关系,外部依赖是应用程序代码外部的项目,可能对应用程序构成威胁。这些项目通常仍在组织的控制范围内,但可能不在开发团队的控制范围内。在研究外部依赖关系时,首先要考虑的是如何在生产环境中部署应用程序。

这包括查看应用程序是如何运行的。例如,如果应用程序预期运行在已经按照组织的强化标准进行了强化的服务器上,并且预期位于防火墙之后,则应该记录这些信息。

  1. 步骤2:确定威胁并对其进行排名

识别威胁的关键是使用威胁分类方法。可以使用威胁分类(如 STRIDE)或应用安全框架(ASF),它定义了威胁类别,如授权和日志记录、身份验证、授权、配置管理、存储和传输中的数据保护、数据验证和异常管理。

威胁分类的目标是帮助从攻击者(STRIDE)和防御角度(ASF)识别威胁。步骤 1 中生成的DFD 有助于从攻击者的角度识别潜在的威胁目标,例如数据源、流程、数据流和与用户的交互。这些威胁可以进一步确定为威胁树的根源,每个威胁目标都有一棵树。

从防御的角度来看,ASF 分类有助于将威胁识别为针对此类威胁的安全控制的弱点。常见威胁列表和示例有助于识别此类威胁。使用和滥用案例可以说明如何绕过现有的保护措施,或者哪里缺乏这种保护。

每个威胁的安全风险的确定可以使用基于价值的风险模型来确定,例如基于一般风险因素(例如,可能性和影响)。

综上所述,结合实际,一般有一下四种常见方式来进行具体代码审计:

  1. 根据敏感关键字回溯参数传递过程。
  2. 查找可控变量,正向追踪变量传递过程。
  3. 寻找敏感功能点,通读功能点代码。
  4. 直接通读全文代码。

  1. 敏感函数回溯参数过程

根据敏感函数来逆向追踪参数的传递过程,是目前使用得最多的一种方式,因为大多数漏洞是由于函数的使用不当造成的。另外非函数使用不当的漏洞,如SQL注入,也有一些特征,比如Select、Insert等,再结合From和Where等关键字,就可以判断这是否是一条SQL语句,通过对字符串的识别分析,就能判断这个SQL语句里面的参数有没有使用单引号过滤,或者根据经验来判断。像HTTP头里面的HTTP_CLIENT_IP和HTTP_X_FORWORDFOR等获取到的IP地址经常没有安全过滤就直接拼接到SQL语句中,并且由于它们是在$_SERVER变量中不受GPC的影响,那我们就可以去查找HTTP_CLIENT_IP和HTTP_X_FORWORDFOR关键字来快速寻找漏洞。

这种方式的优点是只需搜索相应敏感关键字,即可以快速地挖掘想要的漏洞,具有可定向挖掘和高效、高质量的优点。

其缺点为由于没有通读代码,对程序的整体框架了解不够深入,在挖掘漏洞时定位利用点会花费一点时间,另外对逻辑漏洞挖掘覆盖不到。

  1. 查找可控变量,正向追踪变量传递过程

前面提到的根据敏感关键字来回溯传入的参数,是一种逆向追踪的思路,也提到了这种方式的优缺点,实际上在需要快速寻找漏洞的情况下用回溯参数的方式是非常有效的。正向追踪变量传递过程,通常是在渗透和代码审计结合着做的时候,或是是复现公开漏洞的时候,在已经找到了漏洞利用点的情况下,来逐步跟进变量的传递过程,确定漏洞代码位置。

  1. 寻找敏感功能点,通读功能点代码

在有了一定的代码审计经验之后,一定会知道哪些功能点通常会有哪些漏洞,在审计人员想要快速挖掘漏洞的时候就可以这样来做:首先安装好并且运行程序,看下程序有哪些功能,这些功能的程序文件分别是怎么样的,是独立的模块还是以插件形式存在,或者是写在一个通用类里面,然后多处调用。在了解这些功能的存在形式后,可以先寻找经常会出问题的功能点,简单黑盒测试一下,如果没有发现很普通、很常见的漏洞,再去读这个功能的代码,这样读起来就可以略过一些刚才黑盒测试过的点,提高审计速度。

根据经验,简单介绍几个功能点经常会出现的漏洞,如下所示:

  1. 文件上传功能

这里说的文件上传在很多功能点都会出现,比如像文章编辑、资料编辑、头像上传、附件上传,这个功能最常见的漏洞就是任意文件上传了,后端程序没有严格地限制上传文件的格式,导致可以直接上传或者存在绕过的情况,而除了文件上传功能外,还经常发生SQL注入漏洞。因为一般程序员都不会注意到对文件名进行过滤,但是又需要把文件名保存到数据库中,所以就会存在SQL注入漏洞。

  1. 文件管理功能

在文件管理功能中,如果程序将文件名或者文件路径直接在参数中传递,则很有可能会存在任意文件操作的漏洞,比如任意文件读取等,利用的方式是在路径中使用../或者..\跳转目录。

除了任意文件操作漏洞以外,还可能会存在XSS漏洞,程序会在页面中输出文件名,而通常会疏忽对文件名进行过滤,导致可以在数据库中存入带有尖括号等特殊符号的文件名,最后显示在页面上的时候就会被执行。

  1. 登录认证功能

登录认证功能不是指一个登录过程,而是整个操作过程中的认证,目前的认证方式大多是基于Cookie和Session,不少程序会把当前登录的用户账号等认证信息放到Cookie中,或许是加密方式,是为了保持用户可以长时间登录,不会一退出浏览器或者Session超时就退出账户,进行操作的时候直接从Cookie中读取出当前用户信息,这里就存在一个算法可信的问题,如果这段Cookie信息没有加salt一类的东西,就可以导致任意用户登录漏洞,只要知道用户的部分信息,即可生成认证令牌,甚至有的程序会直接把用户名明文放到Cookie中,操作的时候直接取这个用户名的数据,这也是常说的越权漏洞。

  1. 找回密码功能

找回密码虽然看起来不像任意文件上传这种可以危害到服务器安全的漏洞,但是如果可以重置管理员的密码,也是可以间接控制业务权限甚至拿到服务器权限的。而找回密码功能的漏洞有很多利用场景,最常见的是验证码爆破,目前特别是APP应用,请求后端验证码的时候大多是4位,并且没有限制验证码错误次数和有效时间,于是就出现了爆破的漏洞。除此之外,还有验证凭证的算法,这需要在代码中才能看到,所以我们做代码审计的时候可以看看这个算法是否可信。

  1. 通读全文代码

前面提到的根据敏感关键字来回溯传入的参数,是一种逆向追踪的思路,我们也提到了这种方式的优缺点,实际上在需要快速寻找漏洞的情况下用回溯参数的方式是非常有效的,但这种方式并不适合运用在企业中做安全运营时的场景,在企业中做自身产品的代码审计时,我们需要了解整个应用的业务逻辑,才能挖掘到更多更有价值的漏洞。

通读全文代码也有一定的技巧,并不是随便找文件逐个读完就可以了,这样你是很难真正读懂这套程序的,也很难理解代码的业务逻辑,首先我们要看程序的大体代码结构,用了什么开发框架,持久层框架是mybatis还是Hibernate,等等。如主目录有哪些文件,模块目录有哪些文件,插件目录有哪些文件,除了关注有哪些文件,还要注意文件的大小、创建时间。我们根据这些文件的命名就可以大致知道这个程序实现了哪些功能,核心文件是哪些。utils工具类,controller类,各个功能接口的实现类,配置文件等都属于重点部分。

在看程序目录结构的时候,我们要特别注意以下几个文件:

1)函数集文件,通常命名中包含functions或者common等关键字,这些文件里面是一些公共的函数,提供给其他文件统一调用,所以大多数文件都会在文件头部包含到其他文件。寻找这些文件一个非常好用的技巧就是去打开index.php或者一些功能性文件,在头部一般都能找到。

2)配置文件,通常命名中包括config关键字,配置文件包括Web程序运行必须的功能性配置选项以及数据库等配置信息。从这个文件中可以了解程序的小部分功能,另外看这个文件的时候注意观察配置文件中参数值是用单引号还是用双引号括起来,如果是双引号,则很可能会存在代码执行漏洞。

3)安全过滤文件,安全过滤文件对我们做代码审计至关重要,关系到我们挖掘到的可疑点能不能利用,通常命名中有filter、safe、check等关键字,这类文件主要是对参数进行过滤,比较常见的是针对SQL注入和XSS过滤,还有文件路径、执行的系统命令的参数,其他的则相对少见。而目前大多数应用都会在程序的入口循环对所有参数使用addslashes()函数进行过滤。

4)index文件,index是一个程序的入口文件,所以通常我们只要读一遍index文件就可以大致了解整个程序的架构、运行的流程、包含到的文件,其中核心的文件又有哪些。而不同目录的index文件也有不同的实现方式,建议最好先将几个核心目录的index文件都简单读一遍。

通读全文代码的好处显而易见,可以更好地了解程序的架构以及业务逻辑,能够挖掘到更多、更高质量的逻辑漏洞,一般老手会比较喜欢这种方式。而缺点就是花费的时间比较多,如果程序比较大,读起来也会比较累。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值