如今,在病毒肆虐、黑客无处不在的网络环境里,软件的安全性已成为一个备受关注的话题。传统的软件安全性关注的主要在于权限与角色的管理,如访问控制,或是数据的机密性与完整性,如加密解密。但是一个软件系统在应用了这些安全措施之后就能确保其万无一失了吗?答案似乎并不那么简单,其中一个重要(甚至可以说致命)的问题在于软件系统中的漏洞。常常在一个看似牢不可破的系统中,仅仅是因为存在一个小小的漏洞,导致整个安全体系被黑客轻易地攻破,整个系统的控制权彻底丧失。
所谓漏洞,通常是指软件中存在的一些bug,但这种bug又不同于普通软件测试中的bug。普通软件测试中的bug指的是功能性或逻辑性的错误,如对话框弹出出错、系统执行某功能失败等。这些bug影响的只是用户的使用体验,并不对系统的安全构成威胁。而软件的安全漏洞指的是某些别有用心的用户非正常的使用软件,让软件执行一些自己精心设计的恶意代码,或解析畸形文件,当软件中存在安全漏洞的时候,程序的正常执行流程被改变,从而达到获取系统的控制权或窃取机密数据的目的。
数据库系统是在操作系统平台之上的最重要的系统软件,数据库系统的安全可以说是十分重要的。曾经有句话这样说:如果网络遍地是金钱,那么金钱就在数据库服务器中。随着无纸化业务环境的不断扩大,人们在数据库中存储着越来越多的敏感信息:银行账户、医疗记录、政府文件、军事机密等等,数据库系统就成为越来越有价值的攻击目标,因此确保数据库系统的安全也越来越重要。
作为一种大型的系统软件,数据库系统中也存在着各种各样的安全漏洞,其中危害性较大的有缓冲区溢出、堆溢出和SQL注入等。
1.缓冲区溢出
缓冲区溢出是一种很常见也很古老的安全漏洞。早在上个世纪80年代,缓冲区溢出就已经为人所知,但时至今日,大量的缓冲区溢出漏洞仍被发现。最著名的Morris蠕虫就是利用Unix系统上fingerd程序的缓冲区溢出漏洞。在Oracle 9i发布之初,Oarcle公司曾宣称他的数据库是“ unbreakable ”的,但不到几个月的时间,就暴出Oracle 9i中oracle.exe、XDB等程序存在多个缓冲区溢出漏洞。
在C语言中最常见的缓冲区就是字符数组,而操纵字符数组的函数有gets、strcpy、sprintf等。这些函数在执行字符串拷贝的过程中没有对字符串进行长度检查,这样就很容易发生超长的字符串溢出缓冲区的情况。当初这样设计是出于效率的考虑,但现在看来,这些函数的使用已成为C语言软件脆弱的一个重要因素。如果程序员没有良好的编程习惯,时刻注意函数调用过程中是否拷贝了超过缓冲区长度的字符串,那么缓冲区溢出就不可避免。对于一个有缓冲区溢出漏洞的程序,当普通用户输入超长字符串时,通常只会使该程序崩溃。例如对于下面一小段代码:
以下是引用片段: /* vulprog */ #include int main(int argc , char * argv) { char buff; strcpy(buff, argv); } |
如果用户执行 ./vulprog AAAAAAAAAAAAAAAA,在Linux上会出现段错误,因为用户输入了超长的字符串,除了填满了缓冲区,还覆盖了其他一些程序正常退出所需要的数据。为了研究这个问题,就需要了解Linux系统中进程的内存空间。
在Linux系统中,2G~3G的内存地址是普通用户进程的加载空间,其内存布局如下图所示:
从图中可以看出栈结构是从高地址增长到低地址,而进行函数调用时系统所作的“序幕”工作就是将函数的返回地址和EBP压栈,再将ESP赋给EBP使其成为局部基指针,最后ESP减去一定的值为局部变量留出空间。这样当程序将过长的字符串拷贝到缓冲区时就会依次覆盖EBP