用vs2005 + vsto 开发一个outlook的addin的时候,碰到了一个问题,在我机器上运行的好好的程序,用vs自己的打包安装程序安装到别的机器的时候总是显示 not load, 加载程序的时候出错。google了一下,遇到了一个新名词, CAS: code access security. 找不到相关的中文文档。在codeproject上看到了这篇文章。文章比较长,有codeproject的文风,讲解的非常详细,就是一点基础都没有的人都知道他说什么,但是要耐心看。 ^_^
Role Based Security (not being discussed in this article) 基于角色的安全验证
Code Access Security 代码访问安全性
CLR 允许代码做那些只被授权的行为,所以, CAS 是一种通过阻止未授权的访问来保护资源和操作的一种安全系统。运用 CAS ,你可以做到:
指定你的代码能够做的
指定那些代码可以代用你的代码
唯一标识你的代码
我们将在这篇文章讨论这些问题,你应该熟悉一些术语。
术语
CAS 包含一下这些元素:
-
permissions
许可
-
permission sets
许可集
-
code groups
代码组
-
evidence
物证
-
policy
策略
Permissions
Permissions 声明对受保护的资源的访问或者是执行受保护的操作的能力。 .Net Frameword 提供了一些 Permission 的类,像 FileIOPermission (对文件起作用), UIPermission (允许使用一些用户接口), SecurityPermission (对于执行代码甚至是绕过安全机制这是必须的)。我将不会列举所有的 Permission 类在这里,他们列举在下面。
Permission sets
一个
Permission Set
是一个
permission
的容器。你可以把
FileIOPermission
和
UIPermission
放到你的
Permission set
中,然后叫他“
My_PermissionSet
”。一个
Permission set
可以包含许多
permissions
。
FullTrust
,
LocalIntranet
,
Internet
,
Execution
和
Nothing
是
.net framework
内置的一些
permission sets
。
FullTrust
包含所有的
permissions
,而
Nothing
则表示什么都不包括,甚至连执行的权利都没有。
Code groups
Code group
是一个特定条件下的代码逻辑组。
http://www.somewebsite.com/
的代码可以属于一个代码组,包含一个特定的强类型名字的代码属于另一组,特定装配的代码又属于一个组。内置的代码组像
My_Computer_Zone
,
LocalIntranet_Zone
,
Internet_Zone
等等。像
permission sets,
我们可以创建代码组来满足我们基于
evidence
的需求。
Site
,
Strong Name
,
Zone
,
URL
是一些
evidence
的类型。
Policy
Security policy
是可配置的规则的集合,当我们决定赋予代码一定的许可。有
4
个机制的等级
Enterprise
,
Machine
,
User
和
Application Domain
,每种操作不互相依赖。每个等级有它自己的
code groups
和
permission sets
。他们的层次如下:
【图一】
Ok
,理论说完了,让我们把理论付诸实践。
小例子
让我们创建一个新的
windows
应用程序。添加
2
个
button
到存在的
form
上。我们将用到文件系统,所以添加
System.IO
命名空间。
using System.IO;
【图二】
写下如下代码:
{
StreamWriter myFile = new StreamWriter( " c:\\Security.txt " );}
myFile.WriteLine( " Trust No One " );
myFile.Close();
private void btnRead_click( object sender, System.EventArgs e)
{
StreamReader myFile = new StreamReader( " c:\\Security.txt " );}
MessageBox.Show(myFile.ReadLine());
myFile.Close()
完整的版本号我们的例子才能够工作。确定你设置了你的版本号为一个固定的值,否则它会随着你的编译次数自动增加。我们标记这个装配用一个强名字,这将被用做 evidence 时标识代码用。这就是为什么你需要设置你的版本号为一个固定的值。
[assembly: AssemblyVersion( "<chsdate isrocdate="False" islunardate="False" day="30" month="12" year="1899" w:st="on">1.0.0</chsdate>.0" )]
就这样。。。没有什么异常。它将会写一个名字为 security.txt 的文件到 c:. 现在跑起来你的代码,它将会创建一个文件并写下一行,每件事看起来都 ok 。。。除非你没有 c 盘。现在我们要做的事把我们的装备到一个 code group ,并且设置一些 permissions 。还不要删除 security.txt 文件,我们晚点会需要它。
NET Configuration Tool
我们可以用两种方式来做这个工作,从 .NET Configuration Tool 或者是用命令行调用 caspol.exe 。 首先我们用 .NET Configuration Tool 。从控制面板——》管理工具-》 Microsoft .NET Framework Configuration 。 你也可以用“ mscorcfg.msc ”在 .net 命令行。你可以做很 cool 的事情通过这个工具。。但是目前我们只是对设置代码访问权限感兴趣。
【图三】
Creating a new permission set 创建一个新的 permission set
展开
runtime security policy
节点。你可以看到安全机制级别
Enterprise
,
Machine
和
User
。我们将改变
Machine
机制的安全集合。首先我们创建我们自定义的
permission set
。右击
permission sets
节点选择
new
。因为我到别的易记住的名字,我命名它为
MyPermissionSet
.
【图四】
我将在下一个屏幕添加 permissions 到 permission set 。在左边的 panel ,我们可以看到 .NET Framework 支持的所有的 permissions 。现在我们得到 File IOpermission 属性。设置 File path 到 c:\ 然后只选上 read 。所以我们没有赋予写的权限。请注意那里还有另一个选项说“准许装备自由的访问文件系统”如果这个被选上,任何事情都可以做,没有任何限制。
【图五】
现在我们多添加
2
个
p’ermissions
Security
和
User
Interface
。
只需要添加记住添加“准许装备自由的访问文件系统”。我很快将会解释这些属性。没有
Security
permission
。我们没有权限去执行我们的代码,没有
User Interface p’ermission
,我们将不会看到
UI
。如果你添加了这
3
个
permissions
,你会看到有一个新的
permission set
被创建,命名为
MyPermissionSet
.
Creating a new code group 创建一个新的代码组
现在我们创建一个
code group
然后设置一些条件,所以我们的装配将会是这个
code group
的一员。注意在
code group
机电,
All_Code
是父节点。右击
All_Code
节点选择
new
。创建
code group
向导将呈现在你面前。我将命名它为
MyCodeGroup
.
【图六】
下一个屏幕,你需要提供一个
condition
类型给
code group
。这些就是之前提到过的
evidence
。在这个例子中,我们用
strong name
类型。首先标识你的装配用一个
strong name
然后编译它。现在我们按
import
(导入)按钮选择你的装配。
Public key
,名字和版本将从装配中萃取出来,我们不需要考虑它。现在我们到下一个页面。我们必须为我们的
code group
指定一个
permission set
。因为我们已经创建了一个
MyPermissionSet
,
,我们从
list box
中选他。
【图七】
Exclusive and LevelFinal
如果你还没有被。
NET configuration
默认的安全设置搞混淆,你的装配已经属于另一个内置的
code group
My_Computer_Zone
。当
permission
被计算,如果一个特别的装配属于多个
code group
在相同的机制级别,最后的
permissions
将是所有的
code group
的
permissions
的集合。我将晚点解释怎么计算
permissions
,现在我们只需要跑我们的装配用我们的
permission set
,就是用
MyPermissionSet
关联的
MyCodeGroup
.
所以我们必须设置其他的属性来达到这个目的。右击新创建的
MyCodeGroup
节点选择属性。选择
check box "
This policy level will only have the permissions from the permission set associated with this code group.
"
这个机制等级的
permission
只包含与这个
code group
关联的
permission set
。这个叫
Exclusive
属性。如果这个被选上了,运行时将不会允许除关联到这个
code group
的
permissions
。另一个选项叫做
LevelFinal
.
这两个属性将在计算
permissions
的时候起作用,我们将详细解释它在下面。
【图八】
我们已经设置了许多属性,但是它将在最后讲得通。 :-<
好了,现在我们来跑我们得代码。我们现在做得就是把我们得代码放到一个 code group 中,并且给她对 c 盘得只读得权限,跑它并按那两个按钮, read 工作得很好,但是当你按 write 得时候,一个 exception 抛出了,因为我们没有对 c 盘写得权限。下面时出错得信息。
【图九】
所以多谢 code access security ,这种对资源得限制是可行的。通过 CAS 你可以做很多事情,我们讲在余下得文章中讨论。
Functions of Code Access Security
根据文档, CAS 充当了一下这些功能:(直接从文档上复制过来的)
-
Defines
permissions
and
permission sets
that represent the right to access various system resources.
-
Enables administrators to configure
security policy
by associating sets of permissions with groups of code (
code groups
).
-
Enables code to
request
the
permissions it requires in order to run
, as well as the
permissions that would be useful to have
, and specifies which
permissions the code must never have
.
-
Grants permissions
to each assembly that is loaded, based on the permissions requested by the code and on the operations permitted by security policy.
-
Enables code to
demand
that its callers have specific permissions. Enables code to demand that its callers possess a digital signature, thus allowing only callers from a particular organization or site to call the protected code.
-
Enforces restrictions
on code at run time by comparing the granted permissions of every caller on the call stack to the
permissions that callers must have.
我们已经做了最顶上的
2
个,那是管理的部分。有一个独立的命名空间我们还没有看过
System.Security
,这个是用来实现安全的。
Security Namespace
System.Security
命名空间下的
主要的类有:
Classes
|
Description
|
CodeAccessPermission
|
定义下面的结构所有的代码访问权限
|
PermissionSet
|
申明一个包含不同 permissions 的集合
|
SecurityException
|
当检测到安全错误的时候抛出的异常
|
System.Security.Permissions 命名空间下的主要的类有 :
Classes
|
Description
|
EnvironmentPermission
|
控制对系统和用户环境变量的访问权限
|
FileDialogPermission
|
通过文件对话框控制对文件和文件夹的访问权限
|
FileIOPermission
|
控制对文件和文件夹的访问权限
|
IsolatedStorageFilePermission
|
指定对私有的虚拟文件系统的使用方式 .
|
IsolatedStoragePermission
|
指定对普通的独立存储的访问权限
|
ReflectionPermission
|
控制对
metadata
访问通过
|
RegistryPermission
|
控制对注册表的访问
|
SecurityPermission
|
描叙安全的 permissions 应用到 code 上
|
UIPermission
|
控制 UI 和键盘的权限
|
你能找到更多的
permission
类 在其他的命名空间,例如,
SocketPermission
和
WebPermission
在
System.Net
。
SqlClientPermission
在
System.Data.SqlClient
命名空间
,
PerformanceCounterPermission
在
System.Diagnostics
命名空间等等。
下一步,我们将看到怎么使用这些类。
Declarative vs. Imperative (声明和命令)
你在写代码的时候可以用两种不同的语法,声明和命令。
Declarative syntax 声明语法
生命语法用需要的安全信息属性去标识方法,类或者是装配。所以当编译的时候,他们替换相应的元数据章节。
2
3 public calss MyClass
5 {
7 public MyClass() { } // all these methods
9 public void MyMethod_A() { } // demands unrestricted access to
11 public void MyMethod_B() { } // the file system
13 }
命令语法
命令语法用运行时方法调用创建一个新的实例。
{
public MyClass() { }
public void Method_A()
{
// Do Something
FileIOPermission myPerm =
new FileIOPermission(PermissionState.Unrestricted);
myPerm.Demand();
// rest of the code won't get executed if this failed
// Do Something
}
// No demands
public void Method_B()
{
// Do Something
}
}
他们 2 个的主要的不同是,声明调用是在编译的时候调用而命令调用是在运行时的时候。请 注意编译的时候指的是在 jit 编译的时候。
这里有一些行为可能与 permissions 冲突。
首先,我们看一下声明的语法。用
UIPermission
来说,声明语法意思使用属性,所以我们实际使用
UIPermissionAttribute
,当你打开
msdn
你会看到这些属性:
-
Action - SecurityAction
枚举的一个值
-
Unrestricted –
自由访问
-
Clipboard –
对键盘的访问权限,
UIPermissionClipboard
枚举的一个值
(UIPermission specific)
-
Window –
对窗体的访问权限
UIPermissionWindow
的一个值。
Action
和
Unrestricted
属性是通常类的
permission
。
Clipboard
和
Window
用来指明
UIPermission
。你必须提供
action
你使用其他属性的你用到的
permission
类。所以在这种情况下,你可以像这样写:
Clipboard = UIPermissionClipboard.AllClipboard)]
或者添加
Clipboard
和
Window
两个属性:
Clipboard = UIPermissionClipboard.AllClipboard,
Window = UIPermissionWindow.AllWindows)]
如果你想要声明一个 permission 用自由访问,你可以这样做:
当你使用命令语法,你可以使用构造函数来传递这些值,然后在属性中调用它的
action
。我们用
RegistryPermission
。
new RegistryPermission(RegistryPermissionAccess.AllAccess,
" HKEY_LOCAL_MACHINE\\Software " );
myRegPerm.Demand();
如果你想自由访问这些资源,你可是使用
PermissionState
枚举用如下的方法:
RegistryPermission(PermissionState.Unrestricted);
myRegPerm.Demand();
这是你调用任何 permissions 类所需要知道的在 .NET Framework. 。现在,我们将详细讨论 actions
Security Demands
需求用来保证每一个调用你代码的调用者(间接的或者是直接的),已经得到需要的
permission
。这个通过一个栈来完成。什么
...
一个老鼠道?不,那是你女朋友做的,我的意思是一个栈道。请求一个
permission
,运行时安全系统浏览栈,比较每个调用者准许的
permissions
和被请求的
permission
。如果任何一个调用栈不能找到请求的
permission
,然后
SecurityException
抛出。请看如下的图:
图十】
不同的装配和不同的方法在相同的装配通过栈道来检查。
现在回到需求。这里有 3 种类型的需求。
-
Demand
-
Link Demand
-
Inheritance Demand