代码检查工具在线(源代码安全审计)


【摘 要】 源代码是软件的根本,其存在的安全缺陷是导致软件出现漏洞的根源所在,人为因素的影响使得每个应用程序的源代码都可能存在安全漏洞。从根本意义上来说,代码审计就是挖掘源代码中存在的代码安全问题。本文对源代码安全问题进行分析,阐述了源代码审计方法和流程,通过研究Java Web漏洞产生的原因及源代码表现形式代码检查工具在线,给出了源代码安全编码原则,总结出消除相应漏洞的安全编码规范。

【关键词】 源代码安全审计 漏洞检测 编码规范 静态检测

1 引言

随着计算机信息技术的发展,应用软件呈现开放化、智能化、融合化、多元化等发展趋势。应用软件由于源代码编写不规范或引用的第三方框架、开源组件存在安全漏洞,从而导致软件中隐藏着安全漏洞或质量缺陷。这些安全漏洞或质量缺陷一旦被攻击者利用,将会威胁软件系统和其应用数据的机密性、完整性、可用性。开放式Web应用程序安全项目(Open Web Application Security Project,OWASP)2017年发布的Web应用安全十大漏洞中有8项和源代码缺陷相关。

应用软件和信息系统的安全问题必须从底层源头出发,进行源代码安全审计,检查代码的安全缺陷,编写是否遵循安全编程规范,开发是否使用了不安全的第三方组件代码检查工具在线,从而在安全事件发生前或漏洞隐患尚未被利用前有效规避大部分应用程序的代码问题,提高系统的主动安全防御能力。

2 源代码安全审计的方法和流程

2.1 源代码安全审计方法

源代码安全审计的主要目的是提高源代码质量,通过对程序源代码进行检查和分析,发现源代码在软件设计、测试、应用部署等各阶段中可能存在的安全缺陷或安全漏洞,从源头上避免潜在的安全风险。源代码审计技术可分为静态检测(Static Analysis Security Testing,SAST)、动态检测(Dynamic Analysis Security Testing,DAST)及动静结合检测(Interactive Application Security Testing,IAST)。

静态检测是指在不运行程序代码的情况下,对程序中数据流、控制流、语义等信息进行分析,配合数据流分析和污点分析等技术,对程序代码进行抽象和建模,分析程序的控制依赖、数据依赖和变量受污染状态等信息,通过安全规则检查、模式匹配等方式挖掘程序源代码中存在的漏洞。源代码漏洞静态检测方法一般是将源代码转化为单词(token)、树、图等代码中间表示形式,结合不同的检测算法和检测模型进行检测。

动态检测是指向程序输入人为构造的测试数据,根据系统功能或数据流向,对比实际输出结果与预想结果,分析程序的正确性、健壮性等性能,判断程序是否存在漏洞。动态检测技术主要分为3种:模糊测试、动态符号执行和动态污点分析。

动静结合检测是一种将静态分析和动态分析相结合的混合式漏洞检测方法,先使用静态检测方法对大规模的软件源代码进行检测,对大规模的软件源代码进行切分,有针对性地进行检测。再使用动态检测方法对已划分的程序代码进行数据输入,根据数据流向来判断漏洞是否存在。

2.2 源代码静态审计思路

源代码审计的思路有以下3个。根据敏感函数来逆向追踪参数的传递过程,即检查敏感函数的参数,然后回溯变量,判断变量是否可控且是否经过严格过滤;正向追踪变量传递过程,观察是否有变量输入到高风险函数中,或传递的过程中是否有代码逻辑漏洞;通读全文代码,根据自身的经验判断漏洞位置,直接挖掘功能点漏洞。

2.3 源代码静态审计流程

常见的静态分析工具有: CodeQL、Fortify、CoBOT、Coverity、 RIPS、FindBugs、Cppcheck等。依据分析目标的不同,静态分析可分为:面向源代码的静态分析和面向二进制代码的静态分析。面向源代码的静态分析以程序的源代码作为输入,对其进行语法分析、语义分析,并转换为某种特定形式的中间表示,基于该中间表示进行数据流分析、控制流分析等。面向二进制代码的静态分析则是以经过反汇编等手段处理后的二进制代码作为输入,运用模式匹配或补丁对比等方式实现漏洞检测,当程序是以二进制形式发布的,需要使用面向二进制代码的漏洞分析。本文主要研究面向源代码的静态检测技术的原理和流程。

源代码通过编译器进行词法分析、语法分析和语义分析,生成抽象的中间表示,这些中间表示蕴含了源代码的特定信息。例如,在语法分析阶段,语法分析器会产生蕴含丰富语法信息的抽象语法树。抽象语法树中叶子节点用于表示操作数,非叶子节点用于表示操作符,树的层次结构表示代码语句间的嵌套关系。为了更有效地捕获代码的语法、语义及上下文信息,学术界提出使用抽象语法树、控制流程图、程序依赖图等代码的中间抽象表示建立漏洞挖掘模型。源代码审计流程如图1所示。

代码检查工具在线

图1 源代码审计流程

2.3.1 词法分析

词法分析阶段是编译前端的第一个阶段,这个阶段的任务就是从左到右逐个字符对构成源程序的字符串进行扫描,即对源代码进行扫描,并按照定义好的词法规则进行解析,识别出一个个token,将源代码分割成由一个个token组成的数组,即形成一个token流。

2.3.2 语法分析

语法分析是在词法分析生成的token序列基础上,识别token序列之间的关系,并表示成一种后续程序处理过程中更容易理解和访问的中间表示形式,我们也称为抽象语法树。同时,将有关源代码信息存放在符号表的数据结构中,符号表和中间表示形式一起用来构造目标程序。

2.3.3语义分析

语义分析在抽象语法树的基础上进行分析,分析时考虑被检测程序的基本语义,根据上下文环境检测源代码中是否存在语义错误。

2.3.4控制流图构建

控制流图反映了程序中各语句之间的先后执行顺序。控制流图是一个有向图G=(V,E),其中V代表节点的集合,E是有向边的集合。为了更清楚地标识一个控制流图,额外加入2个控制流节点:START和STOP,其中START节点为控制流图的入口节点,STOP节点为控制流图的出口节点。控制流图中的节点V代表程序的1条语句,有向边E表示语句的控制流走向。

2.3.5数据流图构建

数据流图可以通过分析抽象语法树直接获得,数据流图能够表示语句和变量之间的数据依赖关系。通过从控制流图中提取控制依赖关系,从数据流图中提取数据依赖关系,将2种关系融合到同一张图中,进而形成程序依赖图。

2.3.6函数调用图构建

函数调用图是对程序中函数调用关系的一种静态描述,在函数调用图中,节点表示函数,边表示函数之间的调用关系。通过函数调用图可以了解程序中的函数及函数之间的调用关系。由于面向对象程序设计的多态性,程序的函数调用图只能大致表示出实际运行时函数的调用关系。

2.3.7数据流分析

数据流分析是指用来获取有关数据如何沿着程序执行路径流动的相关信息的集合。数据流分析的对象是控制流图,通过对控制流图的遍历,从中收集变量的数值产生位置及使用情况等信息,检测数据的赋值与使用是否发生不合理现象,从而检测源代码中潜在的安全漏洞。

2.3.8污点分析

污点分析是一种信息流分析技术,通过对程序中的敏感数据进行标记,跟踪标记数据在程序中的传播,从而检测系统中存在的安全问题。污点分析被定义为三元组,Source即污染源,表示程序从外界获取的不可信输入;Sink即污点汇聚点,通常为一组安全敏感函数,通过数据流分析跟踪程序中污点数据的传播;Sanitizer即无害处理,代表通过移除危害操作等手段使数据传播不再对程序的安全性产生危害。污点分析则是在不运行和不修改代码的情况下,对程序进行语句分析,运用程序变量之间的数据依赖关系,找出污点源(Sources)和污点汇聚点(Sink)之间的传播路径,从而找出程序潜在的漏洞隐患,再通过验证数据方法进行无害处理(Sanitizer)。

3 安全编码原则和规范

本文给出了源代码安全编码原则和源代码安全编码规范,分析了Java Web漏洞的代码缺陷及产生原因,给出了预防结构化查询语言(SQL)注入、跨站脚本攻击、跨站请求伪造、文件上传、命令执行等漏洞的安全编码规范。

3.1 安全编码原则

3.1.1数据输入验证原则

首先假设所有的输入是恶意的,除非能够证明输入数据是安全的,包括来自传感器、文件、用户或数据库等的输入,不仅要限制输入数据的类型、长度、格式和范围,还要验证输入数据是否包含有害信息。

3.1.2身份验证原则

为防止身份信息被窃取,用户身份验证信息在存储、传输等过程中应采取保护措施。在通信通道采取身份验证信息加密保护机制,如使用安全套接层(SSL)方式进行传输;口令的存储不应直接存储密码,而应存储密码的单向散列值,使用用户提供的密码重新计算哈希值,从而减轻字典攻击的威胁。同时限制用户的口令长度和复杂度,以及限制用户登录次数。

3.1.3 最小授权原则

如果恶意程序被注入软件中,进程被赋予的权限大小很大程度上决定了用户能够执行操作的类型。因此,软件中特定对象如进程、用户或计算机程序应被赋予最小权限,任何需要提权的操作,应尽可能保持最短的时间,一旦任务完成,应该立刻收回权限,从而限制潜在的安全风险。

3.1.4配置管理原则

配置管理功能应只有被授权的操作员和管理员才能访问,若确需远程管理的,应使用SSL或虚拟专业网(VPN)等加密通道进行远程管理。对于配置文件、注册表、数据库等关键配置数据应采取数据保护措施,保护关键配置数据的存储和访问的安全。

3.1.5会话管理原则

Cookie可能包括敏感信息,为防止攻击者查看或修改Cookie内容,使用密码对Cookie内容进行加密,从而避免数据受到未经授权的操作。尽量减少会话时长,会话时间越短,攻击者在捕获会话Cookie后,能使用Cookie来访问应用程序的时间就越少,因此可以减少重放攻击和会话劫持风险。

3.1.6组件安全原则

在使用第三方开源组件时,应对开源组件安全性进行评估,避免使用存在已知漏洞的开源组件或对安全漏洞进行修补后使用。

3.1.7内存安全原则

在编程过程中应避免内存出现缓冲区溢出、整数溢出、字符串格式化等问题。

3.2 常见漏洞分析及安全编码规范

3.2.1 SQL注入

3.2.1.1 SQL注入描述

SQL注入漏洞是程序将攻击者的输入参数拼接到了SQL语句中,从而构造、改变SQL语义对数据库进行攻击。

3.2.1.2 SQL注入安全编码规范

(1)加强用户输入验证

在数据请求提交数据库之前,使用处理函数或正则表达式匹配安全字符串的方法过滤用户输入内容中不合法字符。如果返回值属于特定的类型或有具体的格式,那么在拼接SQL语句之前就要进行校验,验证其有效性。未经过滤的用户输入参数不能进行SQL拼接或可扩展语言(XML)拼接。如图2所示,SQL语句将查询字符串常量与用户输入进行拼接来动态构建SQL查询命令。仅当id不包含单引号时,上述查询语句才会是正确的。

代码检查工具在线

图2 禁止未经过滤的参数进行SQL拼接示例

(2)参数化查询

在使用JDBC或Hibernate框架时,使用预编译SQL语句的应用程序接口(API)进行参数化SQL查询。使用MyBatis框架时,使用#{}代替${}进行参数化查询,因为#{}会调用preparedStatement的set方法来赋值;而MyBatis只是将${}替换成变量的值,直接拼接到SQL语句中执行。当无法使用参数化查询,可使用创建白名单规定拼接到SQL查询语句中的数据集合,或使用正则校验限定拼接到SQL查询语句中的数据中可包含的字符集。

使用预编译语句prepared-Statement确保输入值在数据库中当作字符串、数字、日期或布尔值(boolean)等类型,而不是被作为SQL语法的一部分去执行,如图3所示。

代码检查工具在线

图3 SQL语句参数化查询示例

(3)避免直接向用户显示数据库错误信息

避免直接向用户显示数据库错误,如类型错误、字段不匹配等,防止攻击者利用这些错误信息进一步判断数据库的有关信息。

(4)控制访问权限

将普通用户与系统管理员用户的权限严格区分开,坚持最小授权原则,从而最大程度减少因越权而导致的SQL注入攻击。

3.2.2 跨站脚本攻击

3.2.2.1 跨站脚本攻击描述

跨站脚本攻击即XSS攻击,是指前端和后端有交互但没有做输入输出过滤,应用程序将用户发送的不可信赖数据在未经过滤、转义,直接存入数据库或直接输出到页面,从而导致用户Cookie被劫持、构建Get和Post请求、获取用户信息、恶意的JavaScript执行、XSS蠕虫攻击等后果。

3.2.2.2 跨站脚本安全编码规范

跨站脚本攻击可分为持久型XSS、非持久型XSS和DOM型XSS3种类型。攻击者通过XSS漏洞进行劫持用户Cookie、构建Get和Post请求、获取用户信息、XSS蠕虫攻击等。

(1)加强用户输入验证

通过正则表达式限制输入数据中可接受的字符集合。如果输入数据为数字型参数,进行强制类型转换来校验数据的合法性,当输入数据为字符型,则应限制输入数据的长度。如果输入数据存在恶意字符,则拒绝请求。

(2)客户端辅助验证

输入数据的验证首先要在服务端进行,在客户端只能作为辅助手段进行输入数据的验证。

(3)编码处理

在不可信数据输出到页面之前,进行编码处理。使用addslashes()函数对字符串“’”“””“”字符进行转义。使用htmispecialchars()函数对字符串“&”“””“”进行html编码。

(4)防止盗取Cookie

在重要的Cookie中加入HttpOnly属性,使得通过JavaScript脚本无法读取到Cookie信息,有效防止跨站脚本XSS攻击。

3.2.3 跨站请求伪造

3.2.3.1跨站请求伪造描述

跨站请求伪造(Cross Site Request Forgery,CSRF)是指攻击者通过伪装成来自受信任用户的请求,对受信任的网站执行操作的一种攻击方式。

3.2.3.2跨站请求伪造规范要求

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站不拥有所有权,不承担相关法律责任。如发现有侵权/违规的内容, 联系QQ3361245237,本站将立刻清除。