8 个开发更安全代码的简单规则
目前我可以肯定的一点是,任何看过这篇文章的人都会立即发现自己不具备的习惯。这非常好。我知道除此以外还有其他好的想法。这里只是列出我所观察到的!因此,下面介绍的是我在这几年中注意到的几种典型习惯。
习惯 1:承担责任
这是长期以来“没有银弹”观点的一种转变,该观点是 25 年前 Fred Brookes 在其“人月神话”一书中提出的。能否使您的产品具有足够的安全性完全取决于您自己。其他任何人或任何出色的工具或编程语言都无法解决所有安全隐患。不要误解我的意思,我喜欢源代码分析工具,但他们无法神奇般地修复您的所有安全漏洞。只有您自己可以做到这一点。
只有创建安全设计和编写安全代码的开发人员才能构建出安全产品。最后,编写代码由个人完成。工具不能取代个人完成这项工作。因此,您产品的安全性就是您的责任!Blaster 和 CodeRed 蠕虫利用的就是个人编写的代码(参见图 1)。
图 1 有弱点的代号是人编写的
请记住,要仔细检查所有代码,所有代码都有可能受到攻击。没关系。受到攻击也没关系。关键是,您的代码是否会遭到破坏?只有您可以决定最终结果。因此您的代码必须要使您自己满意。您必须对代码的质量充满信心,因而在晚上可以安心地休息,因为您知道如果受到攻击,您已经做好了万全的准备,可以防止代码受到破坏。
如果有可能,最好请一位安全专家对您的代码进行专业评审。不要让那些对安全一无所知的人来检查您的代码,不要期望他们能够找出安全错误和漏洞。要留出充分的时间让真正了解安全性的人检查您的代码。
不要过于自负,在需要帮助时应主动寻求帮助。我刚刚提到了,您不应完全依赖于工具,但您应该利用一切可利用的资源。请对您的代码运行所有可用的源代码分析工具,并经常这样做。利用所有可用的防御性语言构造和库技巧。例如在 C# 语言中,将执行数组访问的面向网络的代码打包,其中数组索引来自网络请求,采用 checked 操作符的形式,以检测可能出现的整数算法错误。
习惯 2:永远不相信数据
对于这一点,我已经说过无数次,但我还要重申一遍:所有输入在得到证明之前都是不可信的。看看那些最让人深恶痛绝的安全漏洞,您就会发现它们所拥有的最显著的共性就是开发人员相信了输入的数据。问题在于您的代码假定这些数据是可靠的,那么如果您的假设不正确将会怎样?在某一天您的应用程序很可能会崩溃。如果严重的话,攻击者可能会在您的流程中插入恶意代码并破坏您的流程。
安全系统的定义是只执行所要求的任务而不执行其他任务的系统。这一定义有些古怪。当输入的数据存在信任问题时,您往往会让系统执行其他任务。常见漏洞和披露 (CVE) 数据 (cve.mitre.org) 的粗略分析显示,从 2001 年至 2004 年,CVE 跟踪的所有安全漏洞中有 47% 的漏洞属于输入信任问题。最显著的问题就是缓冲区溢出、整数算法错误、跨站点脚本和 SQL 插入错误。我们开始不断看到这种漏洞的新变体,如 XPath 插入漏洞和轻型目录访问协议 (LDAP) 插入漏洞。
您可以根据几个简单规则纠正输入信任问题。首先,不要只看您知道的错误,这就假定了您知道所有错误,并可预测将来发生的所有错误。查找错误是可以的,但条件是这不是您唯一的防御手段。更好的策略是将输入控制在您知道的正确的范围内。对于诸如 C# 和 Perl 此类的高级语言,我喜欢使用正则表达式来确保这一点。
其次,抛弃您已知的错误。例如,如果某人通过您的代码远程请求一个文件,并且文件名中包含不确定的字符(如:or \),请拒绝该请求。并且不要告诉攻击者原因;只要说“找不到文件”即可。
最后,净化数据,这一点并非对所有情形都适用。例如,在 Web 服务器中,您应对可能不受信任的输入的输出进行 HTML 编码。
习惯 3:模拟针对您的代码的威胁
您应该有威胁模型对吧?利用威胁模型,您可以了解您的软件可能面临的风险,并确保您有适当的降低风险的举措。但威胁建模带来的益处不仅仅限于安全设计。威胁模型还可以帮助您确保代码质量。威胁模型可以告诉您数据的来源。数据是远程的还是本地的?数据来自匿名的用户,还是来自可信赖的(已验证的)用户、管理员?
通过掌握这些信息,您可以确定您的防御措施是否到位。例如,匿名用户和远程用户都可以访问的代码最好是非常安全的代码。这并不是说只有本地管理员可以访问的代码就应该是不安全的代码,我的意思是远程可访问的代码(尤其是默认情况下运行的代码)必须非常安全,这就意味着要提供更多的防御措施、对代码进行更详细的审查、更注意代码的细节。此外,威胁模型还可以告诉您受保护的数据的特点。例如高价值业务数据和个人可识别的信息应受到严格保护。您的防御措施是否到位?
确保您的威胁模型准确并保持最新,然后确定您的代码的所有入口点,并按可访问性(远程还是本地,高权限还是低权限(或无权限)用户)对其进行排序。首先要对最多人可访问的代码进行最深入的审查。最后沿着匿名数据路径审查所有代码,换句话说,从每个匿名可访问的入口点开始,沿着该路径跟踪数据,检查代码的准确性。
习惯 4:始终提前一步
安全环境总是在不断变化中。似乎每个星期都会出现安全问题的新变体。这就意味着您必须不断演变并了解新威胁和防御措施,否则您就要承受由此带来的后果。
可保持领先的几个简单策略是经常阅读关于软件安全性的优秀书籍。同时从您过去的错误中吸取教训,当然能够从他人的错误中吸取教训则更好。要做到这一点,您可以阅读 bugtraq — 转至 securityfocus.com 并同意通过您的收件箱接收 bugtraq 发布的内容。但请务必采纳以下建议:创建一项收件箱规则,将发布的内容转移到一个特定的文件夹中,以便您进行处理。这一点非常重要。
习惯 5:模糊!
模糊化处理是一项测试技术,旨在找出可靠性错误。经证实,有 1% 的可靠性错误为安全漏洞,很有可能被利用!当然,缓冲区溢出可能会使应用程序崩溃,但假定出现设计完善的恶意负载,可能不会出现应用程序崩溃,攻击者可能会按照他的意愿运行代码。在这一点上我们的格言是“今天拒绝服务就是明天代码的执行”。
偶然的运气或模糊化处理几乎可以找出所有文件解析错误/漏洞。Microsoft 对 XLS、PPT、DOC 和 BMP 等多种文件格式进行了解析,并发现了多个安全漏洞。大多数供应商都存在类似的漏洞,因为对复杂的数据结构进行解析是一项非常复杂的任务,复杂的代码会存在错误,其中一些错误会暴露出安全漏洞。
您必须对解析文件和网络流量的所有代码进行模糊处理。Microsoft 的安全开发生命周期 (SDL) 中就此对文件格式有何意义有非常具体的介绍。您必须利用一个文件模糊处理程序,通过对不正确文件的 100,000 次迭代对所有解析器进行模糊处理。目前有一些比较好的模糊处理程序,在我和 Steve Lipner 合著的“安全开发生命周期”一书 (microsoft.com/MSPress/books/8753.asp) 中,我们提供了一个文件模糊处理程序及 C++ 源代码。
关于模糊化处理还需要注意一点。如果出现了崩溃,不要认为这只是崩溃。这些所谓的崩溃中有很大一部分都是在请求某人编写漏洞。因此不要简单地将一次崩溃认定为“只是一次崩溃”。
习惯 6:不要编写不安全的代码
在 Microsoft,我们使用质量把关的概念来帮助降低开发人员使存在漏洞的代码流入产品的可能性。质量把关是在代码上运行一组源代码分析工具,然后进行登记,对所有问题进行标记。所有发现的问题必须在登记完成前修复。您还可以执行严格的代码规则,如阻止对禁用功能的使用,如不能调用 strcpy 或 strncat,不允许无用的加密。(Microsoft 已经禁用了超过 100 个针对新代码的 C runtime 函数!)例如,就加密算法而言,我们不允许在新代码中使用 DES(密钥长度太短)、MD4 或 MD5(它们目前都已被破解),除非行业标准中规定使用这些算法。
不要重新发明功能。如果您具备对特定文件格式进行解析的代码,那么您无需两套或三套解析代码;只需一套解析代码即可,使其功能足够强大并将其捆绑在一个可在多个项目之间使用的窗体中。
最后,请记住,工具不能取代人来了解如何编写安全代码。这就是安全和隐私教育为何如此重要的原因。您需要全面深入的了解这些概念,对您的工具无法进行的调用和洞察做出判断。
习惯 7:识别策略不对称
这是我最喜欢的一种习惯。记住,作为一名软件开发人员,安全方面的隐患会对您不利。我喜欢称之为“攻击者的优势和防御者的尴尬”您需要确保代码和设计在 100% 的时间内 100% 准确,这是不可能的。更糟糕的是,您还必须在固定的预算内达到这一无法实现的目标,同时还必须考虑到可支持性、兼容性、可访问性和其他“能力”的要求。攻击者会用足够长的时间来找出错误,然后向全世界宣布您的应用程序是不安全的。
在习惯 6 中,我曾经提到,您应该停止编写新的不安全代码。对于习惯 7,您应该关注所有代码,因为攻击者会攻击所有代码,无论是哪个时期的代码。花些时间查看旧代码,找出安全漏洞,并认真考虑受到贬低的旧的不安全功能。如果您使用的是灵活的开发方法,那么您应该考虑派一名或多名专业人员修复旧代码,使其质量与新代码持平。
习惯 8:尽可能使用最佳工具
最后,尽可能使用最佳工具。我喜欢源代码分析工具,并且喜欢所有能够帮助我编写出更安全代码的技术。正如我提到的,工具并非万能的,但它们会有所帮助。会有很大帮助!工具还可以帮助衡量源代码分析得出的问题。工具可以快速扫描大量代码,比人工速度要快得多。而且,这还可以使您感觉到某些代码有多么“差”。
我喜欢的一种技巧就是使用可能的最高警告级别编译代码,例如在使用 Visual C++® 时使用 /W4 警告级别,或者在使用 gcc 时使用 –Wall 警告级别。如果您在代码中发现了大量警告,那么可能该代码还存在编译程序或其他工具没有发现的其他错误。对于这种代码,在提供代码前应该对其进行更详细的安全检查(参见习惯 3)。
我发现我所尊敬的 Microsoft 内部和外部的开发人员具备了这八种良好的习惯。这些习惯本身不会使您成为一流的安全开发人员,但它们肯定会对您有所帮助!