本教程讨论了 .NET Framework 安全性并显示了在 C# 修改安全权限的两种方式:命令性安全和 声明性安全。
教程
大多数应用程 序和组件开发人员不需完成任何特别的工作就可使用 .NET F ramework 安全系统并从它所提供的安全保护中受益。
但“安全库”是一个例外,它要求了解更多深入的知识 并对安全系统加以特殊考虑。这些代码表明了安全托管代码与非限制 代码之间的界限,后者如本机代码(这超出了 .NET Fram ework 安全基础结构可以强制的范围)。这些库通常必须受到 高度信任才可以工作,它们是托管代码中编程错误会潜在地暴露安全 隐患的一个位置。代码访问安全性无法消除人为错误的可能性,但相 对于使用几个安全库的大量应用程序代码而言,需要严格审查的代码 量将大大减少。
示例
该教程包括 下列示例:
示例 1:强制式安全性< br>
示例 2:声明式安全性
示例 3:禁止安全性
安全性
.NET Framework 安全性通过在托管代码上强制实 施安全限制来防止其他代码误用或破坏您的代码和数据。当 .NE T Framework 应用程序请求权限时,由管理员建立的安 全策略将授予权限或拒绝运行代码。信任是基于有关代码的证据(如 数字签名、代码来自何处等)的。一旦授予权限,安全性就会实施控 制这些代码可以做什么(或如果未授予权限,则控制代码不能做什么 )的权限。
权限
.NET Fr amework 安全性只有当代码具有使用受保护资源的“权限” 时才允许它使用这些资源。为了表示此情形,.NET Frame work 使用了“权限”的概念,它表示代码访问受保护资源的权 力。代码请求所需的权限,然后由 .NET Framework 应用的安全策略确定实际授予代码哪些权限。
. NET Framework 提供了代码访问权限类,其中每个类 都封装了访问某一特定资源的能力。使用这些权限向 .NET F ramework 指示需要允许代码做什么以及必须授权代码的调 用方做什么。策略还使用这些对象确定要向代码授予哪些权限。
策略
安全策略的强制性保证了 .N ET Framework 托管代码的安全。每个加载的程序集都 受到安全策略的约束,安全策略基于信任授予代码权限,而信任又是 基于有关代码的证据的。有关管理安全策略的信息,请参见“阅读材 料列表”中的 .NET Framework 文档。
< br>在 C# 中请求安全权限
在 C# 中请 求安全权限的方法有以下两种:
强制式 :使用对 .NET Framework 中权限类的调用
声明式:使用安全权限属性
下面两个示例说明了这两种方法。有关请求安全权限的更多信息,请 参见要求。
示例 1:强制式安全性
以下示例使用 .NET Framework 调用拒绝 U nmanagedCode 权限。
// Imp erativeSecurity.cs
using Sy stem;
using System.Security ;
using System.Security.Per missions;
using System.Runt ime.InteropServices;
cl ass NativeMethods
{
// This is a call to unmanaged code. Executing this method r equires
// the Unmanag edCode security permission. Wi thout this permission
/ / an attempt to call this meth od will throw a SecurityExcept ion:
[DllImport("msvcrt .dll")]
public static e xtern int puts(string str);
[DllImport("msvcrt.dll")]
internal static extern int _flushall();
}
class MainClass
{
pr ivate static void CallUnmanage dCodeWithoutPermission()
{
// Create a sec urity permission object to des cribe the
// Unmana gedCode permission:
SecurityPermission perm =
new SecurityPermis sion(SecurityPermissionFlag.Un managedCode);
/ / Deny the UnmanagedCode from our current set of permissions .
// Any method tha t is called on this thread unt il this method
// returns will be denied access to unmanaged code.
// Even though the CallUnmanag edCodeWithPermission method
// is called from a s tack frame that already
// calls Assert for unman aged code, you still cannot ca ll native
// code. Because you use Deny here, the permission gets
/ / overwritten.
perm .Deny();
try
{
Cons ole.WriteLine("Attempting to c all unmanaged code without per mission.");
Nat iveMethods.puts("Hello World!" );
NativeMethod s._flushall();
Console.WriteLine("Called unma naged code without permission. Whoops!");
}
catch (SecurityException )
{
Console.WriteLine("Caught Secu rity Exception attempting to c all unmanaged code.");
}
}
priv ate static void CallUnmanagedC odeWithPermission()
{ // Create a security permission object to describe the
// UnmanagedCo de permission:
Secu rityPermission perm =
new SecurityPermission( SecurityPermissionFlag.Unmanag edCode);
// Che ck that you have permission to access unmanaged code.
// If you don't have perm ission to access unmanaged cod e, then
// this cal l will throw a SecurityExcepti on.
// Even though the CallUnmanagedCodeWithPermi ssion method
// is called from a stack frame that already
// calls A ssert for unmanaged code, you still cannot call native
// code. Because you use Deny here, the permission get s
// overwritten.< br> perm.Assert();
< br> try
{
Console.WriteLine ("Attempting to call unmanaged code with permission.");
NativeMethods.puts( "Hello World!");
NativeMethods._flushall();
Console.WriteLin e("Called unmanaged code with permission.");
}
catch (SecurityExcept ion)
{
Console.WriteLine("Caught S ecurity Exception attempting t o call unmanaged code. Whoops! ");
}
}
< br> public static void Main ()
{
// The method itself will call the s ecurity permission Deny
// for unmanaged code, w hich will override the Assert permission
// in th is stack frame.
Sec urityPermission perm = new
SecurityPermissio n(SecurityPermissionFlag.Unman agedCode);
perm.Ass ert();
CallUnmanage dCodeWithoutPermission();
< br> // The method itsel f will call the security permi ssion Assert
// for unmanaged code, which will ov erride the Deny permission in< br> // this stack frame .
perm.Deny();
CallUnmanagedCodeWithPe rmission();
}
}
< br>输出
Attempting to cal l unmanaged code without permi ssion.
Caught Security Exce ption attempting to call unman aged code.
Attempting to ca ll unmanaged code with permiss ion.
Hello World!
Called unmanaged code with permissio n.
示例 2:声明式安全性
这 是使用安全权限属性的同一个示例。
// Dec larativeSecurity.cs
using S ystem;
using System.Securit y;
using System.Security.Pe rmissions;
using System.Run time.InteropServices;
c lass NativeMethods
{
// This is a call to unmanage d code. Executing this method requires
// the Unmana gedCode security permission. W ithout this permission,
// an attempt to call this me thod will throw a SecurityExce ption:
[DllImport("msvc rt.dll")]
public static extern int puts(string str);
< br> [DllImport("msvcrt.dll" )]
internal static exte rn int _flushall();
}
class MainClass
{
// The security permission att ached to this method will deny the
// UnmanagedCode p ermission from the current set of permissions for
// the duration of the call to th is method:
// Even thou gh the CallUnmanagedCodeWithou tPermission method is
/ / called from a stack frame th at already calls
// Ass ert for unmanaged code, you st ill cannot call native code. // Because this function is attached with the Deny per mission for
// unmanage d code, the permission gets ov erwritten.
[SecurityPer mission(SecurityAction.Deny, F lags =
SecurityPerm issionFlag.UnmanagedCode)]
private static void CallUn managedCodeWithoutPermission()
{
try
{
Console .WriteLine("Attempting to call unmanaged code without permis sion.");
Native Methods.puts("Hello World!");
< br> NativeMethods._ flushall();
Con sole.WriteLine("Called unmanag ed code without permission. Wh oops!");
}
catch (SecurityException) {
Con sole.WriteLine("Caught Securit y Exception attempting to call unmanaged code.");
}
}
// The security permission attached t o this method will force a
// runtime check for the unmanaged code permission when ever
// this method is called. If the caller does not have unmanaged code
// permission, then the call wil l generate a Security Exceptio n.
// Even though the C allUnmanagedCodeWithPermission method is called
// fr om a stack frame that already calls
// Deny for unman aged code, it will not prevent you from calling
// na tive code. Because this method is attached with the Assert // permission for unmana ged code, the permission gets overwritten.
[SecurityP ermission(SecurityAction.Asser t, Flags =
Security PermissionFlag.UnmanagedCode)]
private static void Ca llUnmanagedCodeWithPermission( )
{
try
{
Consol e.WriteLine("Attempting to cal l unmanaged code with permissi on.");
NativeMe thods.puts("Hello World!");
NativeMethods._fl ushall();
Conso le.WriteLine("Called unmanaged code with permission.");
}
catch (Sec urityException)
{ Console.WriteLin e("Caught Security Exception a ttempting to call unmanaged co de. Whoops!");
}
}
public stati c void Main()
{
SecurityPermission perm = new
SecurityPe rmission(SecurityPermissionFla g.UnmanagedCode);
// The method itself is att ached with the security permis sion
// Deny for u nmanaged code, which will over ride
// the Assert permission in this stack frame .
perm.Assert();
CallUnmanagedCodeWith outPermission();
// The method itself is atta ched with the security permiss ion
// Assert for u nmanaged code, which will over ride the Deny
// p ermission in this stack frame.
perm.Deny();
CallUnmanagedCodeWithPer mission();
}
}
输出
Attempting to call unmanaged code without permis sion.
Caught Security Excep tion attempting to call unmana ged code.
Attempting to cal l unmanaged code with permissi on.
Hello World!
Called unmanaged code with permission .
安全性和性能
.NET Fr amework 安全系统可以防止从网络上下载的恶意代码损坏您 的计算机系统。但即使代码从不引发 SecurityExcep tion,这些安全检查也并不是没有代价的。
通 常,公共语言运行库验证非托管方法的调用方在运行时是否对每个非 托管方法调用都具有非托管代码访问权限。这对多次调用非托管代码 的应用程序来说代价可能是非常高的。SuppressUnman agedCodeSecurityAttribute 可以更改 此默认行为。当用该属性声明非托管方法时,将在调用该方法的代码 被加载到公共语言运行库中时检查安全请求。
安全说明 使用 SuppressUnmanagedC odeSecurityAttribute 时,应格外小心以确 保没有引入安全漏洞。例如,开发人员需要验证用户正在安全使用非 托管 API 以及调用方无法影响或滥用此调用。开发人员还可以 添加适当的请求以确保所有调用方都具有适当的权限。例如,如果对 本机代码进行调用以访问文件(目的是利用结构化存储,如扩展文件 属性等),而非托管代码请求被禁止,则应明确发出一个文件 IO 请求以确保代码不会被误用。
示例 3:优化非 托管调用
在本例中,对非托管代码权限的检查仅在 加载时执行一次,而不是在每次调用非托管方法时都执行。如果多次 调用非托管方法,则可能极大地提高性能。
// SuppressSecurity.cs
using S ystem;
using System.Securit y;
using System.Security.Pe rmissions;
using System.Run time.InteropServices;
c lass NativeMethods
{
// This is a call to unmanage d code. Executing this method requires
// the Unmana gedCode security permission. W ithout this permission,
// an attempt to call this me thod will throw a SecurityExce ption:
/* NOTE: The Sup pressUnmanagedCodeSecurityAttr ibute disables the
c heck for the UnmanagedCode per mission at runtime. Be Careful ! */
[SuppressUnmanaged CodeSecurityAttribute()]
[DllImport("msvcrt.dll")]
internal static extern in t puts(string str);
[Su ppressUnmanagedCodeSecurityAtt ribute()]
[DllImport("m svcrt.dll")]
internal s tatic extern int _flushall();
< br>}
class MainClass
{
// The security perm ission attached to this method will remove the
// Unm anagedCode permission from the current set of permissions fo r
// the duration of th e call to this method.
// Even though the CallUnmanag edCodeWithoutPermission method is
// called from a st ack frame that already calls // Assert for unmanaged code, you still cannot call na tive code.
// Because t his method is attached with th e Deny permission for
/ / unmanaged code, the permissi on gets overwritten. However, because
// you are usin g SuppressUnmanagedCodeSecurit yAttribute here, you can
// still call the unmanaged methods successfully.
/ / The code should use other se curity checks to ensure that y ou don't
// incur a sec urity hole.
[SecurityPe rmission(SecurityAction.Deny, Flags =
SecurityPer missionFlag.UnmanagedCode)]
private static void CallU nmanagedCodeWithoutPermission( )
{
try
{
// The UnmanagedCode security check is disbled on the call
// below. However, the unmanaged call only displays UI. The
// sec urity will be ensured by only allowing the unmanaged
// call if there is a UI permission.
UIPermission uiPermission = < br> new UIPermis sion(PermissionState.Unrestric ted);
uiPermiss ion.Demand();
Console.WriteLine("Attempti ng to call unmanaged code with out UnmanagedCode permission." );
NativeMethod s.puts("Hello World!");
NativeMethods._flusha ll();
Console.W riteLine("Called unmanaged cod e without UnmanagedCode permis sion.");
}
catch (SecurityException) {
Con sole.WriteLine("Caught Securit y Exception attempting to call unmanaged code.");
}
}
// The security permission attached t o this method will add the
// UnmanagedCode permissi on to the current set of permi ssions for the
// durat ion of the call to this method .
// Even though the Ca llUnmanagedCodeWithPermission method is called
// fro m a stack frame that already c alls
// Deny for unmana ged code, it will not prevent you from calling
// nat ive code. Because this method is attached with the Assert
// permission for unmanag ed code, the permission gets o verwritten.
// Because you are using SuppressUnmanage dCodeSecurityAttribute here, // you can call the unma naged methods successfully.
// The SuppressUnmanagedC odeSecurityAttribute will let you succeed,
// even i f you don't have a permission.
[SecurityPermission(Se curityAction.Assert, Flags = < br> SecurityPermissionFl ag.UnmanagedCode)]
priv ate static void CallUnmanagedC odeWithPermission()
{ try
{
Console.WriteLine( "Attempting to call unmanaged code with permission.");
NativeMethods.puts(" Hello World!");
NativeMethods._flushall();
Console.WriteLine ("Called unmanaged code with p ermission.");
}
catch (SecurityExcepti on)
{
Console.WriteLine("Caught Se curity Exception attempting to call unmanaged code. Whoops!" );
}
}
public static void Main( )
{
Securit yPermission perm = new
SecurityPermission(Sec urityPermissionFlag.UnmanagedC ode);
// The met hod itself is attached with th e security permission Deny
// for unmanaged code, which will override the Assert permission in
// th is stack frame. However, becau se you are using
// SuppressUnmanagedCodeSecurity Attribute, you can still call the
// unmanaged met hods successfully.
/ / The code should use other se curity checks to ensure that y ou
// don't incur a security hole.
perm .Assert();
CallUnman agedCodeWithoutPermission();
// The method its elf is attached with the secur ity permission
// As sert for unmanaged code, which will override the Deny
// permission in this sta ck frame. Because you are usin g
// SuppressUnmanag edCodeSecurityAttribute, you c an call the
// unman aged methods successfully.
// The SuppressUnmanage dCodeSecurityAttribute will le t you succeed,
// ev en if you don't have a permiss ion.
perm.Deny();
CallUnmanagedCodeWithP ermission();
}
}
输出
Attempting to ca ll unmanaged code without Unma nagedCode permission.
Hello World!
Called unmanaged co de without UnmanagedCode permi ssion.
Attempting to call u nmanaged code with permission.
Hello World!
Called unm anaged code with permission.
代码讨论
请注意,以上示例允许两个 非托管调用都成功,即使第一个调用不具有 UnmanagedC ode 权限。在使用 SuppressUnmanagedCo deSecurityAttribute 时,应使用其他安全检 查以确保不会招致安全漏洞。在上面的示例中,通过在非托管调用之 前添加 UIPermission 的 Demand:
uiPermission.Demand();
做到这一点,它确保调用方具有显示 UI 的权限。