定义接口
与其他 Web 应用程序相比,XML Web 服务器应用程序的一个主要优点就是很好地定义了传递到您的应用程序的整个 XML 架构。对于应用程序设计人员和开发人员来说,这意味着您已经知道 XML Web 服务所必须处理的数据具有有效的格式。如果接收的数据格式不正确,那么 Microsoft® SOAP Toolkit 2.0 或 .NET 框架之类的工具将过滤出该请求,这样您就不必为此担心了。
例如,您不必分析日期输入的语法是否有效。日期必须具有有效的 XSD 格式,否则该请求会被丢弃。您可能需要利用结构验证,因此不要隐藏字符串变量中的结构。利用 XML 的能力和灵活性可以全面描述发送和接收的数据。
不可见的服务器
黑客攻击您的系统时,首先寻找的是信息。此 Web 站点是驻留在 Windows 中还是驻留在其他系统中?是否正在运行 Active Server Pages?是否安装了 Index Server?是否安装了已知的易受攻击的组件?是否安装了已知的易受攻击的 CGI 应用程序?主机是否正在运行 Microsoft® SQL Server?我是否可以对此服务器进行分布式 COM (DCOM) 调用?
对于不希望受到攻击的站点,即使非常聪明的 Internet 用户也应该无法回答上述任何问题。黑客对您的系统了解得越少,对平台的了解也越少,就越难在您的服务器上找到问题。
例如,试想一下,如果黑客只知道 XML Web 服务的 URL 为“http://www.coldrooster.com/ssf/account.asp”,他们能了解什么呢。由于此 URL 的扩展名为 .ASP,他们可以假设这是一台运行了 Active Server Pages 的 Windows 计算机。根据黑客对 Internet Information Server 的默认配置的了解,他们已具有足够的信息对大量的未正确配置的弱点进行攻击。他们可对配置方法进行大量的、很可能有效的假设,并用这些假设来刺探计算机。
如果 URL 为“http://www.coldrooster.com/ssf/account/”,情况又会怎样呢?在这种情况下,黑客得不到任何服务器所用操作系统的信息,也无从假设系统的配置。将虚拟目录级的请求映射到某个特定的 ASP 页是一个非常小的配置选项,但能为服务器提供很多保护。
熟悉基本 HTTP 协议的用户可能注意到 HTTP 标头特别指明了正在使用的 Web 服务器类型。是的,这是另一种确定计算机上操作系统的更为复杂的方法,但是也可以编写 ISAPI 过滤器来删除或替换此标头。有关如何进行这种操作的信息,请参阅 Developing ISAPI Filters(英文),以及 IIS 程序员指南中的 SF_NOTIFY_SEND_RESPONSE(英文)通知。服务器上运行的基础系统越难辨认,黑客编写的用于在 Internet 上查找某种类型计算机的脚本失败的可能性就越大。
但是操作系统本身并不是唯一的弱点。您创建的 ASP 页在后端执行 SQL 查询并抛出异常时,会执行什么操作?您是否将异常信息返回给用户浏览器?这样不仅指出了 Web 服务器平台,还指出了数据库平台。除此之外,它还可以使用户了解您正在进行的特定 SQL 查询,并为他们提供信息,使其了解如何更改要输入到窗体中的内容以得到他们不应有权访问的信息。
出现其他 COM 异常情况怎么办?如果将异常信息传播给用户,黑客将会知道您的计算机上安装了哪些 COM 组件。如果此 COM 对象是第三方 DLL,则黑客可以得到它的一个副本,并竭尽全力搜索可能存在的所有弱点。这至少使黑客有机会了解服务器上可能存在的问题。
同样,黑客可能会利用触发 COM 异常这一事实来阻塞服务器。黑客意识到多数异常代码路径未经充分测试,且常常是资源泄露或变得更糟的起因。要避免出现这种情况,服务器上的代码应能捕获所有异常情况,并且应该只返回普遍性的错误。对于 XML Web 服务,您应返回几乎不带有平台信息的 SOAP 错误。您可能希望以某种方式将数据连同 ID 一起返回给用户以便将错误与审核日志中的记录进行比较,但是,请将错误详细信息放在审核日志中而不是放在返回的 SOAP 错误中。建议应用程序设计人员创建自己的应用程序的 SOAP 错误架构,同时提供一个非常短的选项列表,并且仅返回此列表上的错误。调用 XML Web 服务的客户端不必知道有关 SQL 查询异常的详细信息,他们只需要知道出现了 SOAP 服务器错误。
如果正在使用 Microsoft® SOAP Toolkit 2.0,可以在 COM 对象上实现 ISoapError 接口以返回您希望返回的确切的 SOAP 错误,而不是一般的工具包错误。一般的工具包错误可以提供大量的、有关错误出现时在服务器上所发生情况的信息。在开发阶段,工具包错误对于调试来说是很有用的,但是它们在产品服务器上不应出现。他们可以为黑客提供大量的、具有潜在破坏性的信息。
XML Web 服务的用户需要知道您的服务所发送和接收的 SOAP 消息的格式,以及您的服务的终点位置。这就足够了。任何其他信息都只会为潜在的黑客提供攻击手段以毁坏您的系统。要进行自我保护,应限制返回与平台有关的信息,并消除计算机上的多余内容,包括删除任何有助于他人识别您的系统的默认虚拟目录或脚本映射。
开发问题
对黑客来说,服务器的脆弱性与服务器上运行的代码质量是成反比的。它包括基础系统(操作系统、Web 服务器和正在使用的 SOAP 工具)的质量,以及为特定应用程序编写的代码的质量。还可能包括服务器上运行的所有其他代码,即使该代码不是应用程序的一部分。但是从开发人员的角度而言,我们希望考虑我们能控制的问题,以及能执行哪些特殊的操作以保证代码的高质量,避免增加 XML Web 服务和服务器上正在运行的其他所有应用程序的脆弱性。
缓冲区溢出
服务器上最可怕的攻击类型是远程用户可以执行恶意代码导致系统完全破坏的攻击。大多数此类攻击都是由于缓冲区溢出错误造成的。这种错误的典型示例是:在 C 代码中为某本地变量分配了固定长度,然后该代码将 HTTP 请求中的信息复制到该变量中。如果您认为请求中的数据不会比您设定的值大,而对您的服务器的恶意请求可以超过该值,并导致其发送的数据在写入到已分配的缓冲区时超出末端。对于本地变量,缓冲区存储在堆栈中,而堆栈中还存储了当前函数结束时要返回的代码地址。通过写入时超出本地变量缓冲区的末端,黑客可以覆盖返回地址并使函数返回到他想要的任何地址,包括 Windows CreateProcess 函数的地址。要传递到 CreateProcess 函数的参数也会存储在堆栈上,如果黑客覆盖了存储这些参数的位置,则可以有效启动服务器上他们想要启动的任何应用程序。
要避免这类攻击,请不要对从请求中读取的数据做任何假设,然后确保缓冲区的处理代码中没有错误。对于 C/C++ 程序员,应对以下函数格外小心:strcpy、strcat、memcpy、gets、sprintf、scanf 以及所有这些函数的变体(如 lstrcpy、wcscpy、CopyMemory 等等)。请注意 strncpy 函数及其他“n”函数只是略好一些,因为在这些方案中长度变量常常是由程序决定的,而且仍有可能覆盖固定长度的缓冲区。“n”函数中的长度参数应根据输出缓冲区的大小而定,而非传入字符串的大小。如果要使用这些函数,请使用“n”版本并从堆中动态分配您的缓冲区以避免可能的堆栈溢出。另外,Microsoft® Visual Studio® .NET C 编译器支持新的 /GS 开关,该开关可使代码不会出现许多常见的缓冲区溢出问题。
利用 .NET 框架和管理代码,可以消除大多数潜在的缓冲区溢出问题。Microsoft 设计 .NET 框架时,意识到了垃圾回收的好处,以及如何消除由传统的缓冲区处理方式引起的问题,所以他们提供了管理代码的能力。必须注意管理代码和非管理代码间的交互问题。但是至少可以在您需要时适当保证应用程序的安全。
检查错误
检查所有调用函数的返回代码。如果正在调用 Win32 API,请确保调用已成功完成。如果正在分配内存,请确保未返回 NULL 值。如果正在进行 COM 调用,特别是正在使用 Microsoft® Visual Basic® 进行调用并且已指定“On Error Resume Next”语句,请确保未出现异常。同样,不要对这类调用的结果做任何假设。
如果正在调用 Win32 安全函数,应特别小心。例如,如果正在调用 ImpersonateLoggedOnUser,应检查返回代码是否存在错误,否则将难以为用户提供较高的安全环境。当您的 Web 应用程序配置为在 IIS 上以“进程内”方式运行时要格外注意这一点,因为代码可能以本地系统帐户运行,这在本地计算机上几乎没有限制。还应注意某些交叉的 COM 调用同样可能在具有较高权限的线程中运行。
尽早、尽快地验证输入
XML Web 服务接收请求时,您应做的第一件事就是验证提交的数据。根据架构进行 Web 服务说明语言 (WSDL) 验证将会捕获许多错误,但是您应立即执行所需的任何应用程序级的验证。包括检查特殊字符、检查特定范围内的数值、检查字符串长度,等等。即使认为所有请求必须来自特定的应用程序,也应在进行证明之前假定传入数据是无效的。事实是对 XML Web 服务的请求可以来自任何地方。如果对数据进行假设,应假定数据可能来自某个恶意用户。
参数验证代码能够快速地完成也是很重要的。识别无效请求的速度越快越好。否则 XML Web 服务容易遭受拒绝服务攻击。如果您的服务器处理非法请求的时间较长,则很可能不能为合法请求提供服务。始终应用与专用数据同等的安全级别来对待消耗时间和资源的操作。如果必须执行耗时的 SQL 查询,或者某个操作要求具有很强的处理能力,则首先要确保请求的合法性。用户是否是合法用户?对请求进行身份验证不仅能防止无效用户使用您服务器上的资源,并且提供了跟踪审核日志中的错误使用情况的能力,使您可以发现特定用户非法使用资源的情况。如果正在验证输入,应首先验证用户的凭据。如果使用普通 HTTP 身份验证机制,则在代码调用之前就会为您进行用户身份验证。
关闭后门
有时在项目的开发阶段提供一种方法,以便在服务器上解决某些问题是非常合适的。例如,XML Web 服务经常会生成一个密钥以便在多次调用之间标识同一个用户,或在这些调用之间维护某种状态。为方便调试经常会添加一段额外的代码以接受由所有密钥组成的密钥,而不是生成真正的密钥。但是,如果确实出于合法调试的目的提供了能避免某些检测的机制,请确保在 XML Web 服务生效之前删除这些后门。
同样,请避免提供能忽略安全性问题的精巧机制,即使您认为从长远来讲它有助于支持服务的长期运行。请考虑 XML Web 服务正在进行应用程序级身份验证的情况。以下做法可能是很吸引人的:即将秘密的管理员级用户名硬编码到您的代码中,这样就可以使那些忘记了自己的管理员帐户密码的人能够进入系统。但是,第一个用户使用了此后门后,机密就泄露了,此代码的所有其他用户将很容易受到攻击。
事实上,甚至在第一次合法使用后门之前此机密就有可能泄露。如果后门帐户和密码在代码中存储为字符串,则其他人很容易通过交付的二进制文件看到已在代码中定义的任何字符串。如果黑客在某个 DLL 中看到类似“SecretAdministrativeUser”的字符串,他就会怀疑此字符串可能是进入代码的后门并尝试使用它。
最后,关于后门,您不应提供通用方法来收集服务器上的信息。虽然它通常有助于为产品提供支持,但在很多情况下,它会产生相反的结果。不要创建可以查看或下载配置文件或系统中源代码的代码。尽管创建这类代码便于分析服务器上的异常情况,但是黑客也会利用它得到同样的信息。通常,用户名和密码存储在配置文件中,而且很多公司认为其源代码的知识产权是其最宝贵的资产。当您考虑到这种能力通常也能查看服务器上其他应用程序的文件时,上述风险会更大。所以,即使 XML Web 服务代码在这些能力面前是无懈可击的,服务器上仍可能存在较易受到攻击的应用程序。
总结
对 Web 系统的攻击确实发生过,例如红色代码蠕虫病毒及其变体就是非常令人头疼的例子。幸好,可以采取一些措施来减少 XML Web 服务的风险级别。希望我们能使您了解某些可能出现的弱点以及如何避免这些弱点,这样,您就可以创建安全的 XML Web 服务了。