.NET 作为安全组件:构建无漏洞解析器的关键选择

本文探讨了.NET框架在构建安全解析器方面的优势,包括内存安全、算术溢出防护和异常处理机制,同时反驳了性能优先的传统观念,强调安全编码应优先于过早优化。

Parser Central: Microsoft .NET 作为安全组件

在过去的十年左右,计算机行业的很大一部分力量开始追求安全软件。这支由聪明人组成的庞大力量,拥有所有资源和市场力量,却尚未给我们带来安全可靠的计算体验,这应该表明这项任务并非轻而易举。

保护我们日常使用的大量软件栈是一项巨大的工程。这有点类似于试图改变我们使用自然资源和能源的方式,以防止进一步的全球变暖。假设您可能在美国犹他州阅读本文,让我们假设全球变暖是一个实际问题,并且是由人类燃烧几乎所有能找到的化石能源以产生能量引起的。

减缓全球变暖是一项艰巨的任务,更不用说停止或逆转它了。我们需要逐步在全球范围内减少产生二氧化碳副产品的能源生产方法。这不是一夜之间就能改变的事情。由于人们依赖您的燃煤电厂提供的能源,您不能简单地关闭它,让他们受冻。但每次考虑建造新电厂时,您都应该考虑其二氧化碳排放,并且当然应该考虑其他能源生产方法。替代方案,即使用可再生能源发电,起初会显得太昂贵和复杂。主要的是,它似乎提供显著较低的性能,因此您不能真正将其视为燃煤电厂的替代方案。

与能源问题类似,当有人推荐.NET作为新软件项目的运行时环境时,性能论点总是被拿出来挥舞。甚至在软件设计和架构的第一个草图之前(希望在实际编码之前会有一些设计和架构),以及在第一行代码编写之前很久,就会有人争论说,无论要开发什么,都必须用C(或其他非托管语言)编写。

一个阴险的事实是,任何团队中最经验丰富的程序员很可能就是提出这个性能论点的人,反对 whoever 提议使用.NET 完成手头的任务。这可能是由于经验丰富的程序员最不可能以可能成为安全漏洞的方式实现非托管代码。也许这只是程序员的老信念,即任何未编译成本地平台代码的东西性能都不好。然而,程序员及其管理者中的精英统治导致高级程序员的陈述比其他所有人的陈述重要得多,因此团队中的每个人都会“学习”到,出于性能原因,他们不能使用.NET。

可悲的真相是,这样的重复陈述将导致软件栈在未来几十年内仍然容易受到内存损坏和整数溢出的影响。特别是经验丰富的人应该知道,正如Donald Knuth已经指出的:“过早优化是万恶之源。” William Allan Wulf 更进一步说:“以效率之名犯下的计算罪恶(不一定实现它)比任何其他单一原因都多——包括盲目的愚蠢。” 不幸的是,这非常接近真相。

如果您是攻击者或漏洞研究人员,并且试图识别对软件栈的简单攻击,您首先寻找的是解析器。任何处理或与您可以影响的外部提供数据交互的代码将是您的主要目标。如果这段代码是用非托管语言编写的,例如C/C++,您很可能在解析器中找到您要找的东西,而不是其他地方。这是大多数软件崩溃的地方,无论是通过文件格式解析还是协议消息解析。在大多数情况下,复杂的解析发生在任何身份验证和授权甚至可能执行之前,因此 resulting 攻击不仅会产生任意代码执行,而且还将完全匿名。

.NET 提供了几乎所有您需要的安全性,以实现不会导致代码中安全漏洞的解析器。边界错误不会导致内存损坏,因此整个缓冲区溢出漏洞类别消失了。更好的是,边界错误会抛出非常独特的异常,因此您的程序可以专门对它们做出反应。在程序集中检查算术上溢和下溢的选项是第二个强大的武器,可以防止利用符号问题和数据类型转换问题,尽管在Visual Studio 2010中,检查算术上溢和下溢仍然不是新项目的默认设置。通过使用安全代码,用任何.NET前端语言编写,您可以轻松构建非常坚固和健壮的解析器,确保您的输入数据正确,并且不允许攻击者绕过您的检查传递过大的整数。让攻击者 fuzz 您的输入文件直到永远。

那么性能问题呢?

首先,何不先编写安全代码,然后再进行性能测量和优化?很可能,这个在一个方法中,多次嵌套循环遍历您的庞大数据集,无论如何都会消耗大部分CPU时间,无论它用什么语言编写。算法错误在几乎所有足够大的应用程序中都会产生更大的性能影响,无论使用什么编程语言。

其次,安全关键的解析器在应用程序操作期间通常不频繁调用。仔细审查您的解析器实际被调用的频率。当您只在用户请求时读取文件时,用户实际上不太可能注意到您的解析器是用.NET还是非托管代码编写的任何性能差异,除了打开损坏文件的情况,无论是有意损坏还是无意损坏。

在今天的多组件多层应用程序设计中,很容易使用严格编写的.NET解析器确保正确的输入数据集,然后将“规范化”和验证的数据传递给执行计算密集型处理的其他代码。

最后但并非最不重要的是,请记住.NET代码不是在虚拟CPU上执行的,而是在执行之前编译成平台特定代码。这种即时编译是任何托管语言获得真正性能的地方。一些非常聪明的人致力于JIT。当他们找到优化并推出更新的JIT时,所有代码突然运行得更快,不仅仅是您的代码。但更重要的是,您不必做任何事情,它会在后台发生。

我在安全关键情况下使用.NET代码进行解析器只有最好的经验。它不会免除您思考输入数据的可接受和不可接受格式,但它大大简化了验证和检查输入数据的过程。如果出现任何问题,异常将向上传播,您可以从顶层代码安全地丢弃输入。在任何其他情况下,清理和消毒的输入数据集可以立即使用,即使在防御较弱的代码中。

回到全球变暖和保护软件栈之间的类比,应该清楚的是,如果我们关心改变未来的任何事情,我们可能不会以以前的方式构建东西。即使从今天起所有解析器都是安全可靠的托管语言实现,没有任何不安全代码调用,旧软件也需要很长时间才能逐步淘汰。但如果我们继续因可疑原因遵循旧习惯,我们将永远无法接近安全可靠计算的目标。

考虑.NET的能力作为未来软件项目的重要安全组件。

我感谢您可能有的任何反馈,如果您碰巧参加BlueHat Buenos Aires,我会在那里见到您。 -FX

[1] “气候变化联合决议”,2010年常会,犹他州,http://le.utah.gov/~2010/bills/hbillamd/hjr012.htm

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计