3.1.2 受影响的编程语言

3.1.2 受影响的编程语言

1.C语言

C语言是一种灵活、可移植的高级编程语言,已经被广泛使用,但在安全社区中它却是灾星。C语言的一些特性使其易于导致安全缺陷。

首先是C语言的可移植性。高级语言的目标之一就是提供可移植性(Portability)。可移植性要求程序逻辑首先在一种独立于底层硬件架构的抽象层编码,然后再将其转换或编译为对应的底层硬件表示。如果对这些逻辑抽象的语义以及它们是被如何转换到机器层指令的机制不甚了解,很可能就会出现问题。这种理解的缺失会造成不当的假设、安全缺陷以及漏洞。

其次是C语言的目标是成为一种内存耗用微小的轻量级语言。C语言的这种特征使得当程序员误以为某些事情会由C语言自动处理(而实际上并不会)时,就可能会导致漏洞的出现。如果程序员熟悉某些表面看上去相似的语言,如Java、Pascal或者Ada,那他们更容易误以为C会为其提供更多的保护。这些错误的假设导致程序员容易犯这样的一些错误:对数组的越界不加保护,不处理整数操作的溢出和截断,以及用错误的实参数目调用函数等。

此外,一个值得一提的C语言特性就是其缺乏类型安全性。类型安全包括两方面含义:保持性(Preservation)和前进性(Progress)。保持性要求如果变量x的类型为t,那么如果x具有值v,则v的类型也为t。前进性要求对一个表达式的计算不会以非预期的方式进行,即要么得到一个值(且计算结束),要么存在某种方式对其进行继续处理。通俗地说,类型安全就是要求对某特定类型的操作其结果仍然是原来的类型。C语言起源于两种无类型的语言,因此仍然保留着很多无类型或弱类型特征。例如,可以通过显式类型转换将指向某一类型的指针转换为指向另一种类型的指针,而当对转换后的指针进行解引用(Dereferenced)时,其行为就是未定义的。还可以用隐式转换合法地对不同长度的带符号和不带符号的数混合操作,并且产生不可表示的结果。这种类型安全的缺乏导致了很大范围的安全缺陷和漏洞。

总之,虽然包含了一些容易产生安全缺陷的因素,但C语言仍然是一种广为流行的语言。这些问题中的部分可以通过对标准、编译器以及相关工具等的改进加以解决。短期来看,改善现状最有效的方式就是通过让开发人员了解常见的安全缺陷以及相应的缓解策略,教他们如何进行安全的程序设计。从长远来看,必须对C语言标准及兼容编译器做进一步的改进,使其继续作为开发安全系统的可行语言。

在C语言被标准化之前,已经有大量遗留下来的C代码。例如,Sun的外部数据表示(External Data Representation,XDR)库就几乎全部用K&RC编写的。因为其编译器标准较宽松,且其编码风格容易产生漏洞,遗留的C代码包含巨大的安全风险。

现有C语言代码中的很多漏洞是在与标准库函数交互时产生的,而那些函数以现在的标准来看已经不再是安全的了(如strcpy())。遗憾的是,由于这些函数都是标准的,因此它们还将继续得到支持,开发者也将继续使用它们,而这通常会导致有害的结果。

2.其他语言

由于C语言本身存有这些内在的问题,因此很多安全专家推荐使用其他语言,如Java语言。尽管Java语言解决了很多C语言具有的问题,但它仍然容易导致实现层次或设计层次的安全缺陷。Java本地调用接口(Java Native Interface,JNI)允许Java与用其他语言编写的程序库进行交互,使得最终的系统可以由Java和C或C++写成的组件组合而成。然而,程序执行的切换通常会导致重大的安全问题。

考虑到已有的对C语言源代码、编程经验以及开发环境的投资,Java语言往往并非是一个可行的方案。有时出于性能或其他和安全无关的因素的考虑,也可能会选择C语言。不管是什么原因,只要选择了用C和C++开发程序,产生安全代码的重担很大程度上就落到了程序员的肩上。

另一种选择是使用C语言的方言,如Cyclone。Cyclone的设计目标是在保持C语言的语法、类型、语义和惯用法不变的基础上提供与Java同样的安全保证(使得任何合法的程序都难以产生安全漏洞)。Cyclone目前在Intel 32位架构(IA-32)的Linux(Windows通过Cygwin)得到了支持。