3.1 C程序设计基础教学中引入防御性编程
我们通常把能够引起软件做一些“超出设计范围的事情”的bug称为漏洞(Vulnerability)。这里,我们将其分为:
功能性缺陷(bug):影响软件的正常功能,例如,语法错误、执行结果错误等。
安全性缺陷(漏洞):通常情况下不影响软件的正常功能,但被攻击者成功利用后,有可能引起软件去执行额外的恶意代码。常见的漏洞包括软件中的缓冲区溢出漏洞、网站中的跨站脚本漏洞(XSS)、SQL注入漏洞等。
大学一年级的学生处于刚刚接触程序设计的阶段,常常认为程序通过编译就成功了,因而我们在教学中进行防御性编程训练。所谓防御性编程(Defensive Programming),仅仅是指一些编程实践,用于消除程序中的功能性逻辑错误以及对可能引起的安全性缺陷的认识。
我们还需要在后继的课程中融入安全性编程的训练,让学生明白,即使预防或消除了这些缺陷并不能使软件免受攻击,还不能保证软件的安全性。所谓安全性编程是指,开发安全的软件是一个连续的过程,首先需要理解其中的安全问题,还必须将这种理解和知识结合到包括设计、编码、测试和配置在内的软件开发生命周期的各个阶段中。
例如下面的两个例程,它们都能通过编译,但是可以让学生查找并分析其中的错误。
例1
int i;
char buffer[100];
for (i = 0; i < 100; i++) ;
buffer[i] = (char)NULL;
根据笔者的统计,一年级学生能够指出例1程序中循环语句多了“;”的占学生总数的81%。但是能够指出其由此会产生缓存溢出的学生数为6。
例2
#include "stdio.h"
void main()
{ int a=44,b=77;
printf("a=%d,b=%d\n",a,b);
printf("a=%d,b=%d \n");
}
根据笔者的统计,一年级学生能够指出例2中第一个printf调用是正确的,第二个调用中缺少了输出数据的变量列表的学生占学生总数的94%。那么第二个调用将引起编译错误还是照常输出数据?如果输出数据又将是什么类型的数据呢?对于这个问题,几乎无人能够正确回答。为此,在教学中我们补充介绍了相关知识。
类似的一些防御性编程还包括,要求学生掌握其他一些错误的发现及其产生漏洞的原理,如:词法缺陷(包括十进制与八进制数的混淆、形似运算符“=”与“==”的混淆、单字符与多字符符号的混淆等);语法缺陷(包括使用宏定义产生的副作用、条件判断语句后多余分号、遗漏分号、遗漏程序分支的返回值等);语义缺陷(包括内存泄露、错误的使用“野”指针、判浮点数相等、精度损失、越界访问、未使用const保护“只读”属性的变量等等);可维护性缺陷(如不正确或有歧义的注释、未清除已被注释的代码、变量或语句多余等)。
3.2 C/C++程序设计的教学中禁用危险API
在C/C++程序中禁用危险的API,可以有效降低在代码中引入安全漏洞的可能。在微软产品的安全漏洞中,有很大一部分是由于不正确的使用C动态库(C Runtime Library)的函数,特别是由字符串处理函数所导致的。文献[5]给出了微软若干由于不当使用C动态库函数而导致的安全漏洞。微软msdn网站[6]给出了完整的危险API的禁用列表。
虽然一些学者对于在代码中全面禁用危险的API存在着一定的争议。但是不容否认的是,在C/C++程序中禁用危险的API,可以有效降低在代码中引入安全漏洞的可能。在考虑了性能和可移植性的因素下,在开发过程中,应使用StrSafe或Safe CRT中对应的安全函数来替代被禁用的危险API。
(责任编辑:adminadmin2008)