日期:2009-03-09  浏览次数:20424 次

 解决方案

  将FSO组件和删除或改名的方式我们不再过多的加以说明了,这一类的解决方法网络上已经有很多文章介绍了。

  另外还有一种关于ASP的FSO组件漏洞的相应解决方案,即根据用户设置权限。在IIS里,可以设置每个站点的匿名访问所使用的帐号,默认为IUSR_ HostName,这一方法的原理就是针对每一个共享主机用户分别设置一个Windows帐号,如IUSR_HostName1,IUSR_ HostName 2等,然后将每一个用户限制在各自的Web目录下。

  我们仔细的研究一下这种方案,可以发现这个方案无法真正实现安全。因为系统运行ASP时并不是使用的IUSR_ HostName帐号,而是IWAM_ HostName帐号,就象在ASP.NET中使用的用户ASPNET一样。也就是说每个ASP程序所拥有的权限并不是IUSR_ HostName的权限,而是IWAM_HostName用户的权限。这样的方法无法真正的将每个共享主机用户的文件系统访问权限限制在各自的虚拟站点中,每个用户仍然可以访问别人的代码。所以这种方法在ASP.NET中无法真正实现用户之间的安全性。

  在ASP.NET中相应的运行ASP.NET程序的帐号为ASPNET,和上面所说的ASP中的解决方案类似,我们只能限制此用户不能访问系统目录等其他目录,但是无法防止用户访问其他共享主机用户的程序代码,无法从根本上杜绝这种问题。

  那么,有没有真正的解决方案了呢?

  有!这就是.NET Framework 的新特性――代码访问安全性

  为了更好的理解这一问题的解决方法,我们需要先介绍一下.NET Framework的安全机制。然后再结合我们的实际问题来讨论解决方案。

  为了解决安全问题,.NET Framework提供了一种称为代码访问安全性的安全机制。代码访问安全性允许根据代码的来源和代码的标识等属性将代码设置为不同级别的信任代码,同时还详细定义了不同级别的对代码的信任,从而可以详细的对代码设置各自的权限而不是将最大权限赋给所有的代码。使用代码访问安全性,可以减小恶意代码或各种错误的代码带来的严重的系统安全性问题的可能性。您可以设置允许代码执行的一组操作,同样可以设置永远不允许代码执行的一组操作。

  实现代码访问安全性的基础就是JIT(运行时编译)和IL(中间代码)。所以所有以公共语言运行库为目标的托管代码都会受益于代码访问安全性。非托管代码则无法完全使用代码访问安全性。


  下面我们将介绍一下代码访问安全性实现的各种功能:

  代码访问安全性是控制代码对受保护资源和操作的访问权限的一种机制。在 .NET Framework中,代码访问安全性执行下列功能:

  · 定义权限和权限集,它们表示访问各种系统资源的权限。

  · 使管理员能够通过将权限集与代码组关联来配置安全策略。

  · 使代码能够请求运行所需权限以及其他一些有用的权限,以及指定代码绝对不能拥有哪些权限。

  · 根据代码请求的权限和安全策略允许的操作,向加载的每个程序集授予权限。

  · 使代码能够要求其调用方拥有特定的权限。

  · 使代码能够要求其调用方拥有数字签名,从而只允许特定组织或特定站点的调用方来调用受保护的代码。

  · 通过将调用堆栈上每个调用方所授予的权限与调用方必须拥有的权限相比较,加强运行时对代码的限制。

  为了确定是否已授予代码相应的权限,.NET运行库的安全系统将遍历整个调用堆栈,将每个调用方所授予的权限与目前要求的权限相比较。如果调用堆栈中的任何调用方没有要求的权限,则会引发安全性异常,并会拒绝访问和相应的操作。堆栈步旨在防止引诱攻击;在这种攻击中,受信程度较低的代码调用高度信任的代码,并使用高度信任的代码执行未经授权的操作。在运行时要求所有调用方都拥有权限将影响性能,但对防止代码遭受攻击至关重要。若要优化性能,可以使代码执行较少的堆栈步;但是,任何时候这样做时均必须确保不会暴露安全缺陷。

  还存在另外一种代码访问安全性的常见用途,即应用程序将控件从网络 Web 站点直接下载到客户端,这种方式的代码安全性也是可以在客户端进行设置的,根据签名等数据权限证书来确定是不是可以允许下载的控件运行。这种方法类似于ActiveX的安全性设置,但是比之在设置权限更加详细和强大。同JAVA APPLET的沙箱安全机制相比,.NET 的客户端控件可以在本地简单设置后访问客户端的各种资源。由于这一方面的用途不是我们的重点,所以我们在这里就不再更详细的讨论其用途及其实现原理了。

  下面我们就谈谈如何应用这一安全特性来解决ASP.NET中存在的系统安全漏洞。由于我们介绍的系统是共享主机,所以有其特殊性,即系统管理员无法事先给所有的代码赋予相应的权限,因为每个用户都可能有各种权限要求,并且这些要求特殊权力的代码在使用中都可能出现的,所以在权限管理上随时都有各种要求。
因此在权限设置方面,不仅仅是管理员设置,也包括了各个共享主机用户的权限请求,这也正是安全代码机制的一个重要部分。

  请求权限是您让运行库知道代码执行有哪些操作权限的方法。通过将属性(声明式语法)放到代码的程序级范围来为程序集请求权限。

  请求内置权限的代码示例:

//The attribute is placed on the assembly level.
using System.Security.Permissions;
[assembly:PermissionSetAttribute(SecurityAction.RequestMinimum, Name = "FullTrust")]

  将此段代码放在程序的开始部分(namespace声明之前),在编译时就会将请求的权限存储在程序集清单中。加载时,运行库检查权限请求,并应用安全策略规则来确定授予程序集哪些权限。

  虽然我们编写的大部分代码都没有请求权限,其实不管是共享主机形式还是独立服务器形式都应该请求权限,这是因为请求权限有助于确保只将代码需要的权限授予代码。如果没有授予代码额外权限,即使某些恶意代码想利用您的代码来进行安全性破坏,它也无法操作没有赋给您自己代码相应权限的额外系统资源。您只应该请求代码需要的那些权限,而不应请求更多权限。

  代码请求权限之后,系统管理员可以使用"权限查看"工具 (Permview.exe,位于您的.NET Framework的目录的bin目录下) 来检查您的程序集并根据其他条件来设置安全策略以决定是否给您的代码所请求的相应权限。如果您不显式地在代码中请求应用程序需要的权限,那么管理员将很难管理您的应用程序。在权限管理严格的主机上,将无法实现您的代码所要求的功能。

  请求权限会通知运行库应用程序正常运行需要哪些权限,或具体不需要哪些权限。在.NET Framework安装后的默认状态下,所有代码都是FullTrust(完全信任)的。这时是不需要申请任何权限的,但是管理员一旦修改了代码安全,我们使用的磁盘访问就要受到限制了,这是就需要申请相应的权限了。我们上边介绍的文件管理代码就需要具有本地硬盘读写操作的能力,则应用程序必须拥有 FileIOPermission。如果代码不请求 FileIOPermission,在本地安全设置不允许应用程序拥有此权限的主机上,在应用程序尝试磁盘操作时就会引发安全性异常。即使应用程序能够处理此异常,也不会允许它操作磁盘。当然,如果您的代码不访问受保护的资源或执行受保护的操作,则不必请求任何权限。例如,如果代码只根据向它传递的输入来计算结果而不使用任何资源,则不必请求权限。如果您的代码访问受保护的资源但未请求必要的权限,则仍可能允许它执行,但如果它尝试访问某种资源而它又没有必要的权限,则可能在执行过程中失败。