写代码需要时刻考虑代码的安全性,昨天看了代码中容易出现的两个安全问题,在此记录一下。
缓冲区溢出
在C语言中,定义一个数组如:char str[100];,这里分配了100个字节的内存(缓冲区)供我们使用,当我们的代码中出现str[200]=’1′;时,这其实就是一个缓冲区溢出,我们修改了未分配给我们使用的内存中的内容。在C中是不检查数组下标越界的情况(当然如果程序非法修改了其他程序的内存,一般操作系统会报告错误,甚至强制结束程序),相比较而言java就更加安全,但速度却比C慢。
当我们调用一个函数时,栈的内存分配一般简化如下:
[函数中定义的变量] [FP] [ret] [函数参数]
栈中最开始分配的内存是函数中使用的变量,然后是帧指针FP(frame pointer)用来寻址栈帧中的变量,其次是用于返回函数被调用的地方的指针ret,最后是函数参数。
[ret]指针的位置一般就在函数中定义变量的后边,试想,若函数中定义了数组,在C中又没有数组下标越界检查,就存在直接通过修改越界数组中的内容把[ret]指针内容修改的可能。这时,当函数返回时,就会返回到我们设置的地址中去执行我们预先设置的任何代码(通常会把代码直接放在缓冲区中),这就是缓冲区溢出攻击的一种方式,最早的莫里斯蠕虫病毒就利用了缓冲区溢出这个漏洞。
所以我们在写代码的时候要尽量避免缓冲区溢出的问题,例如我的博客《C中读入一行字符串的方法》中用gets和scanf读入字符串就存在缓冲区溢出的问题,替换为用fgets读入字符串就能控制读入字符串的大小,避免缓冲区溢出的问题。
双自由攻击(Double Free Attack)
当在C语言中使用malloc时,释放内存不当,可能就会出现漏洞。双自由攻击漏洞理解相对简单,下面通过代码演示:
int* arr = (int*)malloc(sizeof(int)*10); //分配长度为10的int型数组
/* code */
free(arr);
/* code */
free(arr); //这里会出现漏洞,可能会释放掉一些不该释放的内存
正确的写法:
int* arr = (int*)malloc(sizeof(int)*10); //分配长度为10的int型数组
/* code */
free(arr);
arr = NULL;
/* code */
free(arr); //arr为NULL,这里不会出现漏洞
所以我们在写代码时,每次手动释放内存,一定要在释放后把相应的指针置为NULL,避免出现Double Free的漏洞。