C/C++静态代码安全检查工具

    静态代码安全检查工具是一种能够帮助程序员自动检测出源程序中是否存在安全缺陷的软件。它通过逐行分析程序的源代码,发现软件中潜在的安全漏洞。本文针对 C/C++语言程序设计中容易存在的多种安全问题,分别分析了问题的根源,给出了具体可行的分析及检测方法。最后通过对静态代码安全检查工具优缺点的比较,给出了一些提高安全检查效果的建议。

       软件漏洞的出现,除了程序员缺少编写高质量安全程序的意识外,编程语言本身的不安全性也使得程序员更容易在无意中编写出存在安全问题的代码。在众多编程语言中,C/C++语言是目前公认的最容易引起安全问题的语言,黑客往往就利用这些安全问题产生的漏洞来绕过安全策略,以达到网络攻击的目的。针对这种情况,在程序运行前,采用静态代码安全检查工具对源程序进行安全检查是一种很有效的方法。它面对的是问题本身而非征兆,所以有时它比动态监测更有效。

C/C++ 语言静态代码安全检查工具

静态代码安全检查工具的工作类似于软件测试中的静态测试。它们之间的不同之处在于软件测试是为了找出软件中的错误,而静态代码安全检查的主要目的是解决软件的安全问题,并以查找软件中容易被黑客利用的漏洞为目标。它的基本工作原理是:从前至后逐行读入源程序代码,定位可能的嫌疑,再逐步进行深入分析,直至得到确定的分析结果,最后根据不同的分析结果,依安全策略对其进行处理并报告处理结果。

C/C++ 语言静态代码安全检查原理分析

静态代码安全检查的工作过程是:首先读入不安全函数列表,然后先对欲扫描的源程序进行词法分析。根据不安全函数列表,有些函数会被找出来,并做相应的处理;对于需要进行语法分析的函数再做进一步语法分析,确定这些函数是否会引起安全问题,并做相应的处理。重复此过程直到分析完所有源程序,最后报告结果。

具体地说,针对不同类型的安全问题有以下几个方面的分析处理方法。

2.1 缓冲区溢出问题的解决途径

缓冲区溢出问题是目前软件中存在的最普遍的问题。从目前来看,找出了缓冲区溢出问题也就找出了绝大部分的安全问题。缓冲区溢出的最根本原因就是未检查动态缓冲区边界,当源数据长度超出缓冲区长度时产生溢出。要静态地分析出源程序代码中是否存在此类问题,首先就要计算出缓冲区长度。

针对缓冲区的不同类型,可有以下 4 种方法计算缓冲区长度:

(1) 字符串常量:如“satecode scan”,其缓冲区长度为字符数 1。它有两种存在方式,一是直接在函数中使用,另一种是出现在变量定义或赋值语句中。不论哪种都可以通过语法分析回归法计算;

(2) 静态缓冲区:如buf[1024],buf[MAX_len],对于前一种表示方式,可以直接根据 1024 计算缓冲区的大小。对于第情况,通过检查宏定义、常量定义,一般就可以确定缓冲区的大小;

(3) 动态缓冲区:动态缓冲区可以是通过new 进行分配,也可以通过 allocmalloc 进行分配。对于前一种分配方法,需要考虑所分配的基类型,然后计算缓冲区长度。对于后一种分配方式,可直接通过表达式计算缓冲区大小;

(4) 指针引用:通过引用指针或数组下标,从而引用预先设好的缓冲区的一部分。对于这种情况,先用以上方法求出基缓冲区的大小,再通过表达式求值计算出其偏移。

除此之外,预填数据方法也可以检测出缓冲区溢出。例如:对于 strcpy (buf1, buf2),在调用前加入以下语句:memset

(buf1,'A',sizeof(buf2))。如果 buf2 buf1 大,则在调试阶段发生缓冲区溢出。

此方法的特点是对于可能引起缓冲区溢出的函数,在调试阶段(debug),预填满源缓冲区数据,使溢出发生在调试阶段, 避免将不安全因素带到运行期。

具体地把C/C++中可能引起缓冲区溢出的函数分为以下几类,针对不同类的函数分别采用不同的分析与处理。

3.1.1 两个参数的字符串拷贝函数

此类的函数包括 strcpy_mbscpystrcatwcscat 等。其特点是函数有两个参数,从一个参数向另一个参数拷贝字符串,当目标参数缓冲区长度小于源参数缓冲区长度时,发生缓冲区溢出。处理此类函数采用数据流跟踪的方法检查缓冲区长度。

例如下面一段程序:

(1) void transdata(char *str)

(2) {char buffer[24];

(3) strcpy(buffer,str); /*buf[256] 中的内容拷到buffer[24] 里去*/

(4) 

(5) char buf [256];

6 for(i=0;i<255;i++)/*buf[256]里写入 255 M*/

7 buf[i]='M';

8 buf[255]=0;

9 transdata (buf);

此段程序中,当程序调用 transdata(buf)时就会发生溢出。检查这个错误,首先在遇到 strcpy(buffer,str) 时,检查目的参数buffer,并找到在此之前出现的,检测出它的长度应是24 个字节;接着,再检查源参数 str,发现它是经第 1 char  *str→ 第 9 (buf)-->char buf[256]的数据流得到的,并且检测出其长度是 256 字节。此时,已经能初步确定可能产生溢出了。也可以报告存在缓冲区溢出的位置 3 。但是,如果要更精确地定位,则需要利用语法分析器继续从第 5 行定义数组buf[256]开始检查所有路径,这样就可以检测出在调用transdata(buf)时确实会发生溢出,最后报告造成溢出的路 32567

3.1.2 3 个参数的字符串函数

这类函数包括 memcpystrncpy_mbsncpystrncatwcsncat等。其特点是有 3 个形式参数,例如memcpybuf,"M", count count 说明的字节数大于 buf 的缓冲区长度时,发生溢出。处理此类函数同样采用数据流跟踪的方法。在前面这个例子中,就是要检查并比较 count 的大小是否超过了buf 的缓冲区的大小。

3.1.3 格式化控制的字符串处理函数

这类函数有两种不同的情况:一种包括 printffprintf。其特点是函数不能确定数据参数在什么地方结束,因此缓冲区溢出情况一般发生在说明的参数的个数与格式化字符串不匹配时。此类问题要分析格式化字符串与参数是否匹配。

例如下面的一段程序:

1int   data=1234567890;

(2) printf("data=%d%n\n",data, &data);  /* 显示data 的值, 并把显示字符的长度写到变量data */

(3) printf("data=%d\n",data);此程序正常结果是: data=1234567890

data=10

若第行写成printf("%d%n\n",data),执行时就会把显示内容的长度写到变量data 存储那个数值所在的指向的内存里[2]当然,此地址不能被访问。但如果精心设计这个输入值,就会造成缓冲区溢出攻击。分析时,当遇到 printf 时,先用词法分析器分析并记录两个双引号中含有“%”且非“%%”的个数,然 后分析其参数的个数是否与之匹配,就能发现此类问题。

另一类函数包括sprintfswprintf,它通过格式化字符串进行输出,当字符串缓冲区小于格式化串所说明的长度时,会发生缓冲区溢出。此类问题要检查格式化字符串的动态长度, 并与实际区长度进行比较。

3.1.4 向缓冲区中读入字符串函数

其中一类函数包括 scanffscanfsscanf 等。当说明的缓冲区小于实际读入的字符串长度时,发生缓冲区溢出。分析处理方法:跟踪说明缓冲区的参数在程序中的出现,检查其缓冲区长度,并提示用户使用带有限制输入字符长度的格式化字符串。如程序段:

char buffer[20]; scanf("%s",& buffer);

检查时,先分析出&buffer 所指向的缓冲区的大小,并发现%s 未受限制,说明可能产生溢出。接着采用提示用户使用scanf("%20s",& buffer)替换的处理方法[3]

另一类函数包括 fgetsfgetcgetsgetc。如果限制读入数据大小的参数值超出目标缓冲区长度就会发生溢出。处理类函数采用数据流跟踪方法检查这两个数值。如 fgets  (char  *sint n,FILE *stream)此函数的功能是从输入流 stream 中读入字符,并存到 s 串中。这里,要分析 s n 在程序中的定义,检查 s 的长度是不是小于n 的值。需要说明的是,强烈建议不使用 getsgetc,而是用fgetsfgetc 替代。

3.2 关于内存泄漏问题的解决途径

内存泄漏的原因是动态分配了内存,但没有释放,造成分配的内存不能再被使用。一般的情况是堆内存的泄漏,另外还包含系统资源的泄漏,比如核心态HANDLEGDI ObjectSOCKETInterface [4]

由于泄漏发生在程序运行时,因此要检测出内存泄漏问题不太容易。静态安全检查可以采用控制流跟踪的方法,通过分析所有可能的路径,以达到发现内存泄漏的目的,适用于new/deletealloc/freemalloc/freeGlobalAlloc/GlobalFree 等函数。根据内存泄漏发生方式的不同,可以分以下几种情况分析。

3.2.1 忘记释放内存造成的内存泄漏

动态分配内存后,没有调用 delete free 等释放。这种内存泄漏只要分析所有路径是否存在只使用newmalloc 分配内存,但没有用 delete free 释放内存的情况即可。

3.2.2 delete free 的调用方法不正确造成的内存泄漏

此类问题比较常见,造成的后果也比较严重。处理此类问题采用语法分析的方法进行路径分析。

例如下面一段程序:

void function(int size)

{

char* p= new char[size]; if( size>=512 ){

printf“(  Error!”); return;

}

//using the string pointed by p; delete p;

}

显然程序可能没到出口处就结束了,这样就造成了内存泄漏。

检查这类问题可以先采用 3.2.1 中的方法分析所有路径, 然后再检查是否存在没有使用 delete free 释放内存就结束程序的情况。

3.2.3 隐式内存泄漏

此类问题比较特殊,程序在运行过程中不停地分配内存, 但是直到结束的时候才释放内存。严格地说这里并没有发生内存泄漏,因为最终程序释放了所有已申请的内存。但是对于一个服务器程序,如果不及时释放内存可能导致最终耗尽系统的所有内存。检查此类问题要从释放内存空间处入手,检查释放过程是否只在调用析构函数时出现。如果是,则再分析程序是否会出现不调用析构函数而再分配内存的路径,从而发现是否会发生隐式内存泄漏。需要说明的是,由于此类问题一般在异常的情况发生时出现,程序本身又往往是正常的,所以不太容易静态地检查出来,因此这种方法也只能分析出已知的特殊情况。

3.3 空指针引用问题的解决途径

此类函数包括 openfopen。所谓空指针就是没有指向任何合法的存储空间的指针。如果对打开文件的过程未做检查,在打开文件失败的情况下,就会产生空指针,并被黑客利用。

例如下面一段程序:

FILE *in=null, *out=null;

 

out=fopen("\\Test\testnum1.txt","r"); in=fopen("\\Test\testnum2.txt", "w"); while(! feof(out))

{fputc(fgetc(out),in)};

fopen 打开文件失败时,就会产生空指针。另外,如果文件打开后不检查文件属性,文件属性也容易被修改[5]。分析处理此类问题采用语法分析的方法,检查是否对文件打开过程进行了严格检查。

3.4 随机数问题的解决途径

C/C++程序中涉及许多随机数的选取,但系统提供的rand 是一个伪随机数。其内部的实现使得根据给定的种子产生的输出值可能重复,从而造成了随机数可能被黑客猜到的结果。分析处理此类函数的方法是:建议用其它的健壮的数据源取代。可能产生伪随机数的函数包括randdrand48erand48jrand48lrand48mrand48random 等。

 

4 结 论

C/C++ 语言的静态代码安全检查工具能够在程序运行之前发现源程序潜在的安全漏洞,大大降低了出现安全漏洞的概率,对提高程序的安全性具有重要意义。但是代码检查非常耗费时间,而且静态代码安全检查需要知识和经验的积累。对较复杂的问题,静态代码安全检查工具很可能检查不出来。所以,一方面强烈建议程序员时刻保持高质量程序设计的思想,进行主动防错设计。另一方面,对那些比较重要的系统, 提醒使用多种安全检查手段相结合方法。例如,在静态检查的基础上,再采用动态资源监控、漏洞扫描、入侵检测等方法以确保系统安全。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C/C++语言编程安全子集是一种编程技术,旨在提供一种更加安全的编程方法,以减少程序中的安全漏洞和错误。 为了实现C/C++编程语言的安全子集,需要采取以下几个方面的措施: 1. 内存管理:C/C++语言在内存管理方面存在一些安全隐患,如内存泄漏、缓冲区溢出等。安全子集可以提供更加安全的内存管理工具或技术,如智能指针、内存安全检查等,以减少这些安全风险。 2. 输入验证:C/C++语言对用户输入的验证较弱,容易受到各种攻击,如SQL注入、缓冲区溢出等。安全子集可以提供更强大的输入验证工具或技术,如正则表达式、输入过滤等,以增强程序的安全性。 3. 安全函数:C/C++语言中存在一些不安全的函数,如strcpy()、sprintf()等,容易导致缓冲区溢出等安全问题。安全子集可以提供安全函数替代品,如strncpy()、snprintf()等,以减少这些安全风险。 4. 代码静态分析:安全子集可以提供代码静态分析工具,通过对程序的源代码进行分析,识别潜在的安全漏洞和错误,帮助程序员修复这些问题,提高程序的安全性。 5. 安全编码规范:安全子集可以提供安全编码规范,指导程序员编写安全代码,包括如何处理用户输入、如何进行内存管理等,以减少程序中的安全漏洞。 总之,C/C++语言编程安全子集是一种通过采取各种措施来增强C/C++程序的安全性的编程技术。通过使用这种技术,程序员可以减少安全漏洞和错误,提高程序的可信度和鲁棒性。 ### 回答2: C/C++语言编程安全子集是指使用C/C++语言编写程序时,遵循一些规范和最佳实践,以确保程序的安全性和可靠性。下面是一些常见的C/C++语言编程安全子集的要点: 1. 错误处理:正确处理错误和异常,避免程序中断和崩溃。合理地使用try-catch语句块,及时捕获异常并进行必要的处理和恢复。 2. 输入验证:对所有输入数据进行验证和过滤,防止恶意输入和错误输入导致的安全漏洞。例如,使用正则表达式对输入进行格式检查,限制输入字符长度等。 3. 内存安全:合理地使用动态内存分配和释放操作,避免内存泄漏和缓冲区溢出。使用智能指针和RAII(资源获取即初始化)技术,确保资源的正确释放。 4. 安全算法和加密:在需要处理敏感数据或进行安全传输时,使用安全的算法和加密方法。例如,使用AES加密算法对数据进行加密,确保数据机密性和完整性。 5. 安全库函数:使用安全的库函数来替代不安全的函数,以减少安全漏洞的风险。例如,使用strncpy()代替strcpy(),使用snprintf()代替sprintf()等。 6. 安全编码规范:遵循安全编码规范,使用安全的编码风格和命名规则。例如,避免使用不安全的函数和不规范的变量命名,注重代码的可读性和可维护性。 7. 防止代码注入:对于用户输入的代码或数据,进行严格的验证和限制,避免恶意注入攻击。例如,使用参数化查询代替动态SQL查询,使用过滤器和白名单等。 总之,C/C++语言编程安全子集是一系列的安全编程准则和最佳实践,旨在保护程序免受常见的安全漏洞和攻击。开发人员应该积极采用这些准则,加强代码安全性和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值