从开发人员和管理员的角度看代码访问安全






3.17/5 (6投票s)
2004 年 2 月 19 日
9分钟阅读

50336
从开发人员和管理员的角度审视代码访问安全。
引言
代码访问安全性代表了一种根本上不同的控制对受保护资源访问权限的方式。传统上,像文件系统、数据库或网络访问这样的权限是根据用户的特征来分配的。用户执行的所有进程都会假定拥有一套等同的权限。
然而,像代码移动性和组件的增长这样的开发模型已经证明了这一点是不够的。很明显,不受信任的或远程组件的操作应该比受信任的代码受到更多的限制,这使得权限授予过程颠倒过来。代码访问安全性不看用户的特征,而是看代码的特征,包括其受信任状态,来决定如何授予权限。系统管理员可以配置这个过程的细节以及授予哪些权限。
由于这种灵活性,代码访问安全性是一个复杂的主题。虽然有许多文章对其进行了详细描述,但很容易在众多的术语中感到困惑——权限、权限集、证据、代码组、成员资格条件和策略级别只是需要理解的一些概念。我发现从两个角度来看待这个主题更容易——系统管理员和开发者的角度。虽然理解整体很重要,但大多数复杂性只有在您属于哪个组时才相关。
请注意,CodeProject 上有另一篇类似的关于此主题的深入文章。这篇更侧重于教程方法,是这篇精彩文章的后续!
管理员视角
系统管理员负责管理用户可能尝试运行代码的多台计算机。在组织内部,关于这些代码应该有多少灵活性会有独特的顾虑;任意的 Internet 下载是否应该被允许写入硬盘,或访问企业内的私有数据库?答案很可能是否定的。然而,根据组织的需要,许多权限将被认为是可接受的。管理员负责定义一个安全策略来表示这些选择。从高层次来看,管理员需要问自己以下两个问题:
1. 代码应该如何分组?从本地网络运行的代码应该获得与从 Internet 或本地主机运行的代码不同的权限吗?如果是这样,我们必须在概念上将这些代码类型划分为不同的组。
同样,如果某些代码已数字签名,则必须问这个问题,它是否应该被归入一个“签名代码”组,或者这个组是否应该进一步细分为所有由 Microsoft 签名的代码,以及所有由 Acme 公司签名的代码?同样,这个选择很可能会根据我们是否需要为这两个组的代码分配不同的权限来做出。
2. 应该授予代码组哪些权限?
对于每个代码组,应该授予该组中的代码哪些权限?像 Microsoft 签名的组一样从 Internet 运行的代码是否应该比本地主机上运行的未签名代码获得更高的权限级别?
每个代码组都有一个关联的权限集。管理员精确地决定代码如何被判定属于指定的代码组,以及因此分类应该授予它哪些权限。
证据
每个程序集都可以被视为呈现一组证据。证据包括任何公钥、哈希的存在、源目录和区域。
代码组
如前所述,如果我们决定为所有实例分配相同的权限集,代码就可以分组在一起。“.NET Framework 添加了代码组的概念。每个代码组都有一个成员资格条件集。如果一段代码要成为该组的成员,则必须满足这些条件。例如,默认的‘Microsoft 强名称’组的成员资格条件是程序集必须具有与 Microsoft 的公钥签名相连的强名称。只有符合此条件的已呈现程序集才会成为该组的成员。通过检查上述证据来检查成员资格条件。因此,证据对于管理员和开发者来说都是一个重要的概念。”
在“.NET Framework”的默认安装中有 12 个代码组,这些默认值基于 Internet Explorer 中发现的安全区域概念。每个代码组提供略微不同的权限集;Internet 代码组提供一个非常严格的权限集,前面提到的‘Microsoft 强名称’组将提供一个更灵活的权限集。
请注意,给定程序集可以属于多个代码组。最终的授予将是每个组成员资格授予的所有权限的并集。
策略级别
管理员可以在三个级别配置安全策略——企业、计算机和用户。这种分层系统使得某些策略可以在整个企业范围内实施,例如完全禁止写入文件系统。然后可以为特定的计算机和用户做出进一步的策略决策。例如,给定部门内的计算机可能被限制访问某个 IP 地址范围之外的计算机,并且特定用户子集可能被阻止运行来自 Internet 的任何尝试显示任何对话框的程序。请注意,每个策略级别都会增加进一步的限制,但我们可以以一种精细且灵活的方式进行这些限制。策略存储在 XML 文件中,每个策略级别一个。这些文件的位置在此列出。通常使用命令行 caspol.exe 或图形界面 mscorcfg.cfg 来管理安全策略。
摘要
管理员通过定义代码组来定义本地安全策略。他们指示在代码成为组的成员之前必须满足哪些成员资格条件,以及在确定了组成员资格后,程序集应该被授予哪些权限。运行时负责检查已呈现程序集的证据,并确定满足了哪些成员资格条件。最终的授予集是通过将各个组成员资格授予的所有权限加起来计算得出的。
开发者视角
第二个视角是针对“.NET 平台”的应用程序开发者的视角。开发者不应该关心策略文件如何被操作,以及运行时如何使用它们来决定授予代码哪些权限。虽然有一些理解是有用的,但开发者在大多数情况下应该将问题视为他们的程序集将被授予一个完全超出他们控制的权限集。幸运的是,代码访问安全性允许开发者优雅地处理未授予所需权限或授予了不需要的权限的情况。
证据
证据对开发者也很重要。直观地说,添加像哈希或强名称这样的证据可能会导致更高的权限集,因为它代表了程序集是安全的更高程度的保证。例如,许多开发者会遇到这样的问题:直到他们对程序集进行强命名,他们的代码才能执行某些操作。
权限
权限类是开发者视角中最重要的元素。每个受保护的操作,如文件系统、网络、DNS 和打印机访问,都在“.NET Framework 类库”中表示为权限类。权限可以被实例化和配置,以表示资源的特异性。例如,表示文件系统的权限类FileIOPermission
可以被创建并添加一组可读写路径。然后,可以特别请求运行时允许该权限,不仅对相关的组件,而且对所有直接和间接调用者。
FileIOPermission permission = new FileIOPermission( PermissionState.Unrestricted ); permission.AddPathList(FileIOPermissionAccess.Write, "C:\\temp\\out.txt"); permission.Demand();
也可以关闭该权限。
permission.Deny();
第二种场景对于调用不受信任代码的开发者特别有用。在调用危险组件之前限制对某些资源的访问是值得的。
permission.Deny(); UntrustedComponent.DoSomethingDangerous(); permission.RevertDeny();
请注意,这些声明和拒绝存在于“.NET Framework”基类中。例如,构造 System.Net.Sockets.Socket
类的一个实例并调用 Connect()
方法会产生对 SocketPermission
的声明。
RequestMinimal、RequestOptional 和 RequestDeny
使用属性,开发者可以专门请求其程序集需要的或不需要的权限集。当运行时尝试加载程序集时,会考虑这些请求。任何未能授予所请求权限的情况都可以在一开始得到处理。
[assembly:FileIOPermission( SecurityAction.RequestMinimum,
Read = "c:\\temp" )]
[assembly:FileDialogPermission( SecurityAction.RequestOptional )]
更重要的是,开发者可以明确拒绝特定权限。这允许运行时减少授予组件的权限子集,从而减少某人滥用组件的方式,使其偏离原始开发者意图。在下面的代码中,开发者声明即使授予了硬盘写入权限,也应拒绝访问 Windows 文件夹。这可以防止一个不受信任的恶意组件诱使可能更受信任的组件代表它执行对 Windows 文件夹的访问。这种普遍拒绝将防止组件可能带来的大量安全漏洞。所有库开发者都有责任采取此行动。
[assembly:FileIOPermission( SecurityAction.Deny, Write = "c:\\windows" )]
请求继承者和链接者的权限
开发者可能希望声明任何继承或链接到其组件的组件都必须拥有某些权限。例如,如果开发者设计了一个封装 DNS 访问的类,他们可能希望确保任何继承该组件的组件也拥有访问 DNS 资源的权限——否则它将毫无用处。
[class:DnsPermission( SecurityAction.InheritanceDemand, Unrestricted = true )] class MyDnsClass { … }
同一开发者也可能希望确保任何链接到其 Web 浏览器类的用户都拥有完全访问 Web 的权限。
[class:WebPermission( SecurityAction.LinkDemand, Unrestricted = true )] class MyWebBrowser { … }
优雅地处理权限不足
如果声明了一个权限但未授予,则会引发SecurityException
。捕获这些异常意味着开发者可以优雅地处理问题,要么显示错误消息,要么尝试其他操作。