软件供应链安全:源代码缺陷实例剖析
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.5 缺陷种类划分

为了便于读者更好地理解本书介绍的60种重要且常见的缺陷,我们综合缺陷产生的原因、造成的结果以及表现形式等因素,将其分为3大类:输入验证类、资源管理类、代码质量类。

1.5.1 输入验证类

输入验证类缺陷指程序没有对输入数据进行有效验证所导致的缺陷。通常是因特殊字符、编码和数字表示错误所引起的。从安全角度来看,一切外部来源的数据均应视为不可信的数据,任何输入内容均应在经过严格过滤或验证后,再进行相应的逻辑处理或存储。常见的输入验证类缺陷包括SQL注入、XML外部实体注入、命令注入、XSS(跨站脚本)等,详见本书第2章。

1.缺陷成因

以下概括介绍几种常见缺陷的成因。

(1)注入:几乎任何外部数据源都能成为注入载体,包括环境变量、所有类型的用户参数、外部Web服务等。当攻击者可以向解释器发送恶意数据时,注入缺陷产生。注入缺陷通常能在SQL命令、LDAP命令、XPath命令、OS命令、XML解析器等语句中找到。注入缺陷能导致数据丢失、破坏或泄露给无授权方,使无授权方入侵数据库乃至操作系统。

通常情况下,当应用程序存在以下情况时是脆弱的且易受到注入攻击的。

① 用户提供的数据没有经过应用程序验证、过滤或净化。

② 动态查询语句或非参数化的调用在没有上下文转义的情况下用在解释器中。

③ ORM查询参数中使用了恶意数据,用户在查询时可能获得包含敏感或未授权的数据。

④ 恶意数据直接被使用或连接,如用在SQL语句或命令语句中。

实例1:应用程序在下面存在漏洞的SQL构造语句中使用不可信数据。

实例2:框架应用的盲目信任也可能导致查询语句存在漏洞,如Hibernate查询语句(HQL)。

在这两个实例中,攻击者在浏览器中将id参数的值修改成'anything'OR'1'='1'。

这样查询语句的意义就变成了从accounts表中返回所有的记录。

(2)XML外部实体注入:XML外部实体注入是在对非安全的外部实体数据进行处理时引发的安全问题。攻击者可利用这个缺陷提取数据、执行远程服务器请求、扫描内部系统、造成拒绝服务等。

通常情况下,当应用程序存在以下情况时是脆弱的且易受到XML外部实体注入攻击的。

① 应用程序直接接收XML文件或者接收XML文件上传,特别是接收来自不受信任源的文件,或者将不受信任的数据插入XML文件,并提交给XML处理器解析。

② XML处理器启用了文档类型定义(DTD)。

(3)XSS:当应用程序收到含有不可信的数据时,在没有进行适当的验证和转义的情况下,就将数据发送给一个网页浏览器,或者使用可以创建JavaScript脚本的浏览器API,利用用户提供的数据更新现有网页,就会造成XSS攻击。XSS允许攻击者在受害者的浏览器上执行脚本,从而劫持用户会话、危害网站或者将用户重定向到恶意网站。

2.防范措施

针对输入验证类缺陷,通用的解决方式是对输入进行验证,对输出进行编码。

(1)输入验证导致缺陷的原因是对输入的无条件信任。因此,我们首先需要为所有的输入明确字符集,如UTF-8;其次识别数据源是否可信。我们应尽可能地在可信系统上执行所有不可信数据源的数据验证。在执行验证前将数据按照常用字符进行编码,所有输入参数、URL、HTTP请求头等都是需要验证的对象。验证的内容包括数据是否包含超出预期的字符、数据范围、数据长度、数据类型。在基本字符不满足业务规则时,需要对危险字符(<、>、"、'、%、(、)、&、+、\、\'、\"、.)进行额外的控制,如输出编码、使用特定的安全API等。

(2)编码是按预先规定的方法将文字、数字或其他对象编成数码。对于程序开发而言,输出编码采用一个标准的、已通过测试的规则,通过语义输出编码方式,对所有返回到客户端的来自应用程序信任边界之外的数据进行编码。针对解释器的查询(SQL、XML和LDAP查询)和操作系统命令,净化所有不可信的数据输出,从而有效减少部分安全问题。

1.5.2 资源管理类

资源管理类缺陷指因程序对内存、文件、流、密码等资源的管理或使用不当而导致的缺陷。常见的资源管理类缺陷包括缓冲区上溢/下溢、资源未释放、内存泄漏、硬编码密码等,详见本书第3章。

1.缺陷成因

以下概括介绍几种常见缺陷的成因。

(1)内存资源的不当使用通常会导致程序运行破坏、提升权限等危害。超出内存边界所引发的安全问题有缓冲区溢出和越界访问。其中缓冲区溢出指针对程序设计缺陷,向程序源缓冲区写入使之溢出的内容(通常是超过缓冲区能保存的最大数据量的数据),从而破坏程序运行或趁着中断之际获取程序乃至系统的控制权,根据溢出边界位置的不同,可分为缓冲区上溢和缓冲区下溢;越界访问指当程序访问一个数组中的元素时,如果索引值超出数组的长度,就会导致访问数组之外的内存,出现越界情况。

(2)由于内存、文件、流等资源管理不当导致的安全问题有内存泄漏、释放后使用、二次释放、错误的内存释放对象等。内存泄漏指动态申请的内存没有进行有效的释放;释放后使用主要指申请的内存被释放后继续被使用的情况;二次释放指对已经释放后的内存进行重复释放;错误的内存释放对象指释放的对象并非动态分配的内存。

(3)密码的管理不当会导致敏感信息泄露。程序中的硬编码密码会带来信息泄露、维护困难等问题,弱加密不能有效保护密码。

2.防范措施

(1)对不可信数据进行输入和输出控制。在进行内存操作时,检查缓存大小,以确保不会出现超出分配空间大小的危险。

(2)在内存、文件、流等资源使用完毕后应正确释放资源,多次的资源分配且未合理释放会耗尽资源,导致拒绝服务攻击。

(3)锁定是一种同步行为,可确保访问相同资源时多个独立运行的进程和线程不会相互干扰。所有进程和线程都应遵循相同的锁定步骤。如果没有严格遵循这些步骤,那么其他进程和线程可能以原始进程不可见或无法预测的方式修改共享资源。这可能导致数据或内存损坏、拒绝服务等。

(4)为防范对随机数据的猜测攻击,应当使用加密模块中已验证的随机数生成器生成所有的随机数、随机GUID和随机字符串。保护主要秘密信息免受未授权的访问并使用相关的政策和流程以实现加、解密的密钥管理,使用AES加密算法更为安全。同时,还需要避免使用硬编码密码的方式存储密码。

1.5.3 代码质量类

代码质量类缺陷指由于代码编写不当所导致的缺陷,低劣的代码质量会导致不可预测行为的发生。常见的代码质量类缺陷包括整数问题、空指针解引用、初始化问题、不当的循环终止等,详见本书第4章。

1.缺陷成因

以下概括介绍几种常见缺陷的成因。

(1)整数问题:整数又分为有符号整数和无符号整数,且它们有各自的取值范围。当有符号整数的值超出了有符号整数的取值范围时就会出现溢出,当无符号整数的值超出了无符号整数的取值范围时会发生回绕。

(2)空指针解引用:指针存储的是它指向的变量的地址,而解引用就是引用它指向的变量的值。当指针的值为NULL或者未初始化时,对其进行解引用,会导致程序异常崩溃或者出现其他未定义的行为。

(3)初始化问题:变量在使用前应该被初始化,如果没有进行初始化,那么其默认的值是不确定的,使用这个默认值可能会导致出现未定义的行为。而如果这个变量是指针类型且在没有被初始化的情况下进行了解引用,那么会导致空指针解引用。

一般的初始化问题,根据未初始化变量的位置的不同又可分为返回值未初始化、参数未初始化、赋值操作未初始化等。

(4)不当的循环终止:在循环语句中,循环体被重复执行的次数由循环条件所控制。它是一个标量类型表达式,如果控制表达式的值不等于0,循环条件为true;反之,循环条件为false。而当循环条件设置不当时,会导致死循环的产生。

2.防范措施

代码质量类缺陷是源代码缺陷中非常常见的一类问题,根据不同的表现形式应采取如下有针对性的措施:

在使用整数时,要避免操作结果超出整数的取值范围;

在使用指针时,要判断其是否为空;

初始化问题的发现和解决并不困难,通常在定义或者声明时进行初始化,这是一个良好的编程习惯;

在描述循环问题时,要注意设置恰当的循环条件。