8. 代码访问安全(Code Access Security)
CAS是.NET安全模型的一个组成部分,它决定某段代码是否允许执行,以及它在运行时可以使用什么样的资源。例如,CAS可以防止.NET Web小程序(applet)格式化你的硬盘。
CAS安全策略(policy)围绕两个关键概念:代码组(code groups)和权限(permissions)。每一个.NET配件都是一个特定代码组的成员,每一个代码组都被授予在某个命名权限集(named permission set)里设定的权限。
例如,假如使用默认的安全策略,从某个Web站点上下载的控件属于“Zone – Internet”代码组,这个代码组就具有由“Internet”命名权限集所定义的权限。(自然而然,“Internet”命名权限集代表着一个非常有限的范围的权限)
微软定义了一些默认的代码组,但你可以修改它们,甚至创建你自己的代码组。要想看看机器上定义的代码组,可以命令行方式运行“caspol -lg”。我机器上是这样的:
Level = Machine
Code Groups:
1. All code: Nothing
1.1. Zone - MyComputer: FullTrust
1.1.1. Honor SkipVerification requests: SkipVerification
1.2. Zone - Intranet: LocalIntranet
1.3. Zone - Internet: Internet
1.4. Zone - Untrusted: Nothing
1.5. Zone - Trusted: Internet
1.6. StrongName - 0024000004800000940000000602000000240000525341310004000003
000000CFCB3291AA715FE99D40D49040336F9056D7886FED46775BC7BB5430BA4444FEF8348EBD06
F962F39776AE4DC3B7B04A7FE6F49F25F740423EBF2C0B89698D8D08AC48D69CED0FC8F83B465E08
07AC11EC1DCC7D054E807A43336DDE408A5393A48556123272CEEEE72F1660B71927D38561AABF5C
AC1DF1734633C602F8F2D5: Everything
注意代码组的层次结构。最顶层的是最一般的(“All code”),然后被分成几个子组,每一个子组还可以再细分下去。还要注意的是(这有点违反直觉),子组可以关联有比其父组更放得开的权限集。
使用caspol。例如,假定你信任来自www.mydomain.com的代码,并且,你希望它能够完全访问你的机器,但你又希望对所有其它Internet站点保持默认限制,你就可以在“Zone - Internet”代码组下,加入一个新的子代码组,如下:
caspol -ag 1.3 -site www.mydomain.com FullTrust
现在运行caspol –lg,你将会发现新代码组已经被作为组1.3.1而加入:
...
1.3. Zone - Internet: Internet
1.3.1. Site - www.mydomain.com: FullTrust
...
注意,数字标签(1.3.1)只是caspol自己生成的东西,目的是为了易于以命令行方式查看代码组,底层的运行时永远都看不到这些东西。
8.5 我怎么更改代码组的权限集(permission set)?
使用caspol。假如你是机器的系统管理员的话,你可以在“机器”层进行操作,这意味着你做的更改不但会成为那台机器的默认设置,而且用户也不能够修改放大这些权限。假如你是一个普通用户((不是系统管理员)),你仍然可以修改权限,但只能将权限缩小。例如,为了让Intranet代码能够做它想做的事,你可以这么做。
caspol -cg 1.2 FullTrust
注意,因为这比默认策略(在一个标准系统之中)的权限来得大,你只能在机器层做这个修改,在用户层做这样的修改没什么效果。
可以。使用caspol –ap,定义一个XML文件,它包含有权限集中所要包含的权限。为了节省你一点时间,这儿(http://www.eponymous.eclipse.co.uk/samplepermset.xml)有一个相应于“Everything”权限集的样例文件,你可以修改它以满足你的需要。改好以后,可以这样将它加入到可用的权限集之中:
caspol -ap samplepermset.xml
接下来,为了将这个权限集施加于某个代码组上,可以这么做:
caspol -cg 1.3 SamplePermSet
(默认来说,1.3是“Internet”代码组)
8.7 我遇到了一些CAS麻烦,我如何才能诊断是什么地方出了差错?
Caspol有一些选项可能会派上用场。首先,你可以使用caspol –rsg,叫caspol告诉你配件所属的代码组是什么。同样地,你也可以使用caspol –rsg,来查询是什么样的权限施加于指定的配件之上了。
可以,只要你是一名系统管理员的话。运行caspol -s off 即可。
---------------------------无敌分割线------------------------------------------
下面我们将介绍一下代码访问安全性实现的各种功能:
代码访问安全性是控制代码对受保护资源和操作的访问权限的一种机制。在 .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,在本地安全设置不允许应用程序拥有此权限的主机上,在应用程序尝试磁盘操作时就会引发安全性异常。即使应用程序能够处理此异常,也不会允许它操作磁盘。当然,如果您的代码不访问受保护的资源或执行受保护的操作,则不必请求任何权限。例如,如果代码只根据向它传递的输入来计算结果而不使用任何资源,则不必请求权限。如果您的代码访问受保护的资源但未请求必要的权限,则仍可能允许它执行,但如果它尝试访问某种资源而它又没有必要的权限,则可能在执行过程中失败。
系统管理员在得到了用户的权限申请后,可以根据情况考虑是否赋予用户相应的权限,在这里我们来看一下相应代码权限设置的具体方法。
在我们安装成功.NET Framework之后,在Windows 2000 Server的管理工具里多了两项管理工具: Microsoft .NET Framework Configration和Microsoft .NET Framework Wizards。这两种管理工具要实现的功能差不多,只不过Microsoft .NET Framework Wizards是通过向导方式设置,如果您对于.NET Framewrk的安全性操作不是很熟悉的话,可以使用向导根据系统提示一步步的来设置相关的权限。
![]() Microsoft .NET Framework Wizards界面 |
![]() Microsoft .NET Framework Wizards界面 |
使用Microsoft .NET Framework Wizards可以简单的设置.NET Framework的权限。但是我们为了更好的管理各个代码的权限,还是使用Microsoft .NET Framework Configuration来详细的设置我们所需的权限。
![]() (Microsoft .NET Framework Configuration主界面) |
在Microsoft .NET Framework Configuration中可以设置所有关于.NET Framework的属性。
点击我的电脑,打开下拉菜单,我们可以看到程序集缓存、已配置程序集、远程处理服务、运行库安全策略、应用程序等五项。运行库安全策略设置是我们这篇文章的重点。
我们可以先查看一下程序集缓存,在这里我们可以看到所有的全局程序集缓存,全局程序集缓存中存储了专门指定给由计算机中若干应用程序共享的程序集。在这里我们可以发现我们可以使用的所有的程序集,同时也可以添加和删除某些程序集。详细操作请参见.NET Framework SDK文档。
我们在这里主要讨论的是运行库安全策略。在此策略中,按层次结构由高到低分为四个级别,即:企业、计算机、用户、应用程序。在计算权限授予时,运行库从该层次结构的顶部开始,然后向下进行计算。较低的策略级别不能对在较高级别上授予的权限进行增加,但是可以使权限减少。这就是说如果我们将计算机策略设置为较小的权限时,可以不必更改企业策略就可以使设置的权限生效,也就是说权限检查的顺序是从低级别到高级别,只有在低级别中不存在的设置才会检查上一级的设置。默认情况下,用户策略和应用程序域策略的限制性小于计算机策略和企业级策略。大部分默认策略存在于计算机级别。所以我们需要将默认安装的主机的权限在计算机级别上进行修改,修改的内容根据主机是不是共享主机,主机应用的其他不明代码的可能性来设置。如果是我们讨论的共享主机的话,在计算机级别上就尽量将权限设的小一些,为了避免我们讨论的文件系统安全问题,一定要注意权限中的本地磁盘访问权限。
我们打开计算机策略设置可以发现几个默认的代码组、权限集和策略程序集。
根据需要,我们可以添加代码组和自定义的权限集。
在添加代码组的时候可以选择几种条件,主要的条件类型:默认为All Code、应用程序目录、哈希、强名称、作者、站点等。
对于我们所要讨论的共享主机,我们需要将My_Computer_Zone下的All Code的权限更改为不能进行磁盘读写,在更改之前,我们需要先定义一个权限集。这一权限集的作用就是将我们需要点击权限集,右键快捷菜单中选择新建,会出现一个创建权限集的窗口,这里需要给我们新建的权限集命名。下一步就是将单个权限分配给权限集。如下图所示。
![]() |
在这里我们可以给这个新建的权限集赋予一个的系统权限,如上图所示,可用的权限包括:目录服务、DNS、事件日志、环境变量、文件IO、OLEDB数据库操作、注册表等等。我们主要要说明的是文件IO操作,其他的权限操作可以根据自己的需求来设置。这里我们就不再说明了。
在文件IO的权限设置中我们可以自定义针对每一个目录的权限,这里包括读、写、追加、路径盘等操作,在这里我们可以将我们需要的目录权限添加到列表中。因为我们是利用这一权限使所有没有配置权限的代码不可以进行文件IO操作,所以我们不强文件IO添加到分配的权限中。
新建了这一权限集后,我们更改一下默认设置,即将All Code的权限设置为此新建的权限集,也就是说所有没有在此定义代码都不能访问文件IO系统。
这里需要注意一件事情,因为Microsoft .NET Framework Configuration本身也需要文件IO权限,如果没有单独分配给Configuration一个文件IO操作权限的话,那么您就不能再次使用Configuration来设置权限了,只能重新安装.NET Framework了。所以我们需要将FullTrust权限分配给Configuration所使用的Dll,即mscorcfg.dll。在添加时,成员条件可以选择强名称,使用"导入",到winnt/window .net/framework/versionnumber/下选择mscorcfg.dll。如果需要运行其他配置程序,还需要设置相应的权限,这些系统程序一般都在系统程序集缓存中。
这样我们就完成了一个简单的设置,可以防止任何未经验证的代码访问文件IO系统。这样就从根本上防止了磁盘恶意操作。
如果您今后需要利用这一功能或者有共享主机用户需要使用文件IO功能,那么您可以在Microsoft .NET Framework Configuration中将其加入代码,如果不能使其使用其他功能,可以仅仅设置一个只具有文件IO权限的权限集。如果是共享主机用户您还可以给他分配直接到其所使用的目录的全部读写权限,对于他的日志文档,您可以将读功能分配给用户。通过上边新建权限集时我们可以发现:权限集可以规定到每一个目录的读写权限,所以可以将用户锁定于其可以使用的目录中。当然对于共享主机提供商来说,最好的方法就是自己实现这些功能,然后配置权限系统使用户使用共享主机提供商的程序来实现他们的正常操作,而避免了恶意文件操作。
需要注意的是如果分配给每一个单独的程序相应的权限时,我们最好使用强名称这一方式或者其他的可验证方式,强名称由程序集的标识--其简单文本名称、版本号和区域性信息(如果提供)--加上公钥和数字签名组成。这就需要我们使用Sn.exe 来设置密钥、签名和签名验证。强名称保证了程序是开发人员开发的并且没有被改动。
在进行上面的设置之后,管理员可以根据用户的各种需求来设置不同的代码集和权限集。
-------------------------------无敌分割线----------------------------------
使用 .NET Framework 1.1 配置工具创建新的权限集和新代码组。权限集定义了代码能做和不能做的事情,代码组则将权限集与具体代码关联起来,例如特定的程序集和程序集组。
配置代码访问安全策略来约束文件 I/O
在本步骤中,您将为“FileIO”程序集配置代码访问安全策略,并授予受限的“FileIOPermission”,使其只能访问 C:/Temp 目录中的文件。首先,创建新的权限集,其中包括受限的“FileIOPermission”。然后,创建新的代码组,使用强名称证据将新的权限集与“FileIO”程序集关联起来。
• | 创建新的权限集
|
• | 创建新的代码组
|
使用代码访问安全约束来测试文件 I/O
在本过程中,您将在全局程序集缓存 (GAC) 中安装“FileIO”程序集。然后,运行 Web 应用程序,并设法访问 C:/Temp 内部和外部的文件。在前几步中配置的代码访问安全策略约束了代码,结果只能访问 C:/Temp 目录中的文件。
程序集必须安装在 GAC 中,因为 ASP.NET 与域的非特定程序集一样都加载强名称。由 ASP.NET Web 应用程序调用的所有强名称程序集都必须安装在 GAC 中。有关这一问题的详细信息,请参阅模块 7 构建安全的程序集中的“强名称”部分。
注意:通常,默认的计算机级策略和 ASP.NET 策略将授予安装在 GAC 中的程序集以完全信任。在前几步创建的代码组中分配的“该策略级别将只具有与此代码组关联的权限集中的权限”和“将不计算低于该级别的策略级别”属性确保了程序集不被授予完全信任,而仅有先前创建的“RestrictedFileIO ”权限集所定义的权限。
• | 使用代码访问安全约束来测试文件 I/O
|
-----------------------------------------无敌分割线----------------------
如何才能防止本地用户和计算机策略影响我所作的特定企业策略设置?
用户或计算机管理员可以进一步降低授予企业策略级别中特定程序集的权限级别。您可以通过在企业策略级别使用包含 LevelFinal 属性的代码组来停止此操作。该属性将防止对所应用的策略级别之下的任何管理策略级别进行计算。如果企业策略中的某个代码组以 LevelFinal 属性标记,并且在企业策略的计算过程中应用了该代码组,则计算机和用户策略将不会被计算,并且与该代码组相匹配的程序集权限只是那些由企业策略级别所分发的权限。
执行下列步骤,以便在企业策略的代码组上设置 LevelFinal 属性:
1. | 打开“控制面板”。 |
2. | 打开“管理工具”文件夹。 |
3. | 双击“Microsoft .NET Framework 配置”。 |
4. | 在“Microsoft .NET Framework 配置”工具中,打开“运行库安全策略”节点。 |
5. | 打开“企业”策略级别。 |
6. | 打开“代码组”文件夹,然后右键单击要以 LevelFinal 属性标记的代码组。 |
7. | 选择“属性”选项。 |
8. | 在“常规”选项卡中,选中 Policy levels below will not be evaluated 框。 |
9. | 单击“应用”。 |
如何更改安全策略,使特定的应用程序或软件发行者在客户端计算机上始终接收特定的信任级别?
请按下列步骤执行:
如何更改安全策略,使来自特定区域、程序集或软件发行者的程序集无法在我的企业网络上运行?
请按下列步骤执行:
1. | 打开“控制面板”。 |
2. | 打开“管理工具”文件夹。 |
3. | 双击“Microsoft .NET Framework 配置”。 |
4. | 在“Microsoft .NET Framework 配置”工具中,打开“运行库安全策略”节点。 |
5. | 打开“企业”策略节点。 |
6. | 打开“代码组”文件夹。 |
7. | 右键单击 All_Code 代码组并单击“新建”。 |
8. | 输入新代码组的名称和说明。 |
9. | 选择适当的成员条件。如果要阻止来自特定区域的所有代码运行,请选择 Zone;如果要阻止带有特定 Authenticode™ 签名的所有代码执行,请选择 Publisher。 |
10. | 对于权限集,请选择 Nothing。 |
11. | 完成向导。 |
12. | 右键单击新创建的代码组。 |
13. | 单击“属性”。 |
14. | 在“常规”选项卡中,选择 This policy will only have the permissions from the permission set associated with this code group,以便将该代码组从该策略级别的所有其他代码组中排除。 |
15. | 右键单击“运行库安全策略”节点。 |
16. | 选择“创建部署包”向导,并使用该向导创建“企业”策略级别的部署包。 |
17. | 使用组策略或 Systems Management Server (SMS) 来部署 .msi 文件。有关部署的更多详细信息,请参见问题 #3。 |
如果您使用的是成员条件而不是散列、强名称或发行者,则可能希望从该位置运行 GUI 管理工具或其他 Microsoft 组件。如果是这样,请按下列步骤执行:
1. | 在“运行库安全策略”节点下,打开“计算机”策略节点。 |
2. | 打开“代码组”文件夹。 |
3. | 打开 All_Code 代码组子树。 |
4. | 右键单击 Microsoft_Strong_Name 代码组,然后单击“复制”。 |
5. | 在“运行库安全策略”节点下,打开“企业”策略节点。 |
6. | 打开“代码组”文件夹。 |
7. | 右键单击 All_Code 代码组并单击“粘贴”。 注 这些过程假定未对安全策略的默认状态进行大规模修改。 |
如何测试自己的策略更改?
“.NET Framework 配置”工具包含一个向导,可用于查看当前安全策略授予程序集的权限。请按以下步骤使用该向导:
1. | 打开“控制面板”。 |
2. | 打开“管理工具”文件夹。 |
3. | 双击“Microsoft .NET Framework 配置”。 |
4. | 在“Microsoft .NET Framework 配置”工具中,右键单击“运行库安全策略”节点。 |
5. | 选择“计算程序集”。 |
6. | 浏览至或键入要测试的程序集的位置。 |
7. | 在 Choose the type of evaluation 下,指出您是要查看程序集从策略接收的权限,还是要查看安全策略中的哪些代码组应用到该程序集。 |
8. | 除非您要分析特定策略级别对程序集所接收的整个权限集的影响,否则请保留“所有级别”的策略级别设置。 |
9. | 单击“下一步”,以查看所授予权限的列表或代码组列表。 |