本教程讨论了 .NET Framework 安全性并显示了在 C# 修改安全权限的两种方式:命令性安全和声明性安全。
教程
大多数应用程序和组件开发人员不需完成任何特别的工作就可使用 .NET Framework 安全系统并从它所提供的安全保护中受益。
但“安全库”是一个例外,它要求了解更多深入的知识并对安全系统加以特殊考虑。这些代码表明了安全托管代码与非限制代码之间的界限,后者如本机代码(这超出了 .NET Framework 安全基础结构可以强制的范围)。这些库通常必须受到高度信任才可以工作,它们是托管代码中编程错误会潜在地暴露安全隐患的一个位置。代码访问安全性无法消除人为错误的可能性,但相对于使用几个安全库的大量应用程序代码而言,需要严格审查的代码量将大大减少。
示例
该教程包括下列示例:
- 示例 1:强制式安全性
- 示例 2:声明式安全性
- 示例 3:禁止安全性
安全性
.NET Framework 安全性通过在托管代码上强制实施安全限制来防止其他代码误用或破坏您的代码和数据。当 .NET Framework 应用程序请求权限时,由管理员建立的安全策略将授予权限或拒绝运行代码。信任是基于有关代码的证据(如数字签名、代码来自何处等)的。一旦授予权限,安全性就会实施控制这些代码可以做什么(或如果未授予权限,则控制代码不能做什么)的权限。
权限
.NET Framework 安全性只有当代码具有使用受保护资源的“权限”时才允许它使用这些资源。为了表示此情形,.NET Framework 使用了“权限”的概念,它表示代码访问受保护资源的权力。代码请求所需的权限,然后由 .NET Framework 应用的安全策略确定实际授予代码哪些权限。
.NET Framework 提供了代码访问权限类,其中每个类都封装了访问某一特定资源的能力。使用这些权限向 .NET Framework 指示需要允许代码做什么以及必须授权代码的调用方做什么。策略还使用这些对象确定要向代码授予哪些权限。
策略
安全策略的强制性保证了 .NET Framework 托管代码的安全。每个加载的程序集都受到安全策略的约束,安全策略基于信任授予代码权限,而信任又是基于有关代码的证据的。有关管理安全策略的信息,请参见“阅读材料列表”中的 .NET Framework 文档。
在 C# 中请求安全权限
在 C# 中请求安全权限的方法有以下两种:
- 强制式:使用对 .NET Framework 中权限类的调用
- 声明式:使用安全权限属性
下面两个示例说明了这两种方法。有关请求安全权限的更多信息,请参见要求。
示例 1:强制式安全性
以下示例使用 .NET Framework 调用拒绝 UnmanagedCode 权限。
// ImperativeSecurity.csusing System;using System.Security;using System.Security.Permissions;using System.Runtime.InteropServices;class NativeMethods{ // This is a call to unmanaged code. Executing this method requires // the UnmanagedCode security permission. Without this permission // an attempt to call this method will throw a SecurityException: [DllImport("msvcrt.dll")] public static extern int puts(string str); [DllImport("msvcrt.dll")] internal static extern int _flushall();}class MainClass{ private static void CallUnmanagedCodeWithoutPermission() { // Create a security permission object to describe the // UnmanagedCode permission: SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); // Deny the UnmanagedCode from our current set of permissions. // Any method that is called on this thread until this method // returns will be denied access to unmanaged code. // Even though the CallUnmanagedCodeWithPermission method // is called from a stack frame that already // calls Assert for unmanaged code, you still cannot call native // code. Because you use Deny here, the permission gets // overwritten. perm.Deny(); try { Console.WriteLine("Attempting to call unmanaged code without permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code without permission. Whoops!"); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code."); } } private static void CallUnmanagedCodeWithPermission() { // Create a security permission object to describe the // UnmanagedCode permission: SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); // Check that you have permission to access unmanaged code. // If you don't have permission to access unmanaged code, then // this call will throw a SecurityException. // Even though the CallUnmanagedCodeWithPermission method // is called from a stack frame that already // calls Assert for unmanaged code, you still cannot call native // code. Because you use Deny here, the permission gets // overwritten. perm.Assert(); try { Console.WriteLine("Attempting to call unmanaged code with permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code with permission."); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code. Whoops!"); } } public static void Main() { // The method itself will call the security permission Deny // for unmanaged code, which will override the Assert permission // in this stack frame. SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); perm.Assert(); CallUnmanagedCodeWithoutPermission(); // The method itself will call the security permission Assert // for unmanaged code, which will override the Deny permission in // this stack frame. perm.Deny(); CallUnmanagedCodeWithPermission(); }}
输出
Attempting to call unmanaged code without permission.Caught Security Exception attempting to call unmanaged code.Attempting to call unmanaged code with permission.Hello World!Called unmanaged code with permission.
示例 2:声明式安全性
这是使用安全权限属性的同一个示例。
// DeclarativeSecurity.csusing System;using System.Security;using System.Security.Permissions;using System.Runtime.InteropServices;class NativeMethods{ // This is a call to unmanaged code. Executing this method requires // the UnmanagedCode security permission. Without this permission, // an attempt to call this method will throw a SecurityException: [DllImport("msvcrt.dll")] public static extern int puts(string str); [DllImport("msvcrt.dll")] internal static extern int _flushall();}class MainClass{ // The security permission attached to this method will deny the // UnmanagedCode permission from the current set of permissions for // the duration of the call to this method: // Even though the CallUnmanagedCodeWithoutPermission method is // called from a stack frame that already calls // Assert for unmanaged code, you still cannot call native code. // Because this function is attached with the Deny permission for // unmanaged code, the permission gets overwritten. [SecurityPermission(SecurityAction.Deny, Flags = SecurityPermissionFlag.UnmanagedCode)] private static void CallUnmanagedCodeWithoutPermission() { try { Console.WriteLine("Attempting to call unmanaged code without permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code without permission. Whoops!"); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code."); } } // The security permission attached to this method will force a // runtime check for the unmanaged code permission whenever // this method is called. If the caller does not have unmanaged code // permission, then the call will generate a Security Exception. // Even though the CallUnmanagedCodeWithPermission method is called // from a stack frame that already calls // Deny for unmanaged code, it will not prevent you from calling // native code. Because this method is attached with the Assert // permission for unmanaged code, the permission gets overwritten. [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] private static void CallUnmanagedCodeWithPermission() { try { Console.WriteLine("Attempting to call unmanaged code with permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code with permission."); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code. Whoops!"); } } public static void Main() { SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); // The method itself is attached with the security permission // Deny for unmanaged code, which will override // the Assert permission in this stack frame. perm.Assert(); CallUnmanagedCodeWithoutPermission(); // The method itself is attached with the security permission // Assert for unmanaged code, which will override the Deny // permission in this stack frame. perm.Deny(); CallUnmanagedCodeWithPermission(); }}
输出
Attempting to call unmanaged code without permission.Caught Security Exception attempting to call unmanaged code.Attempting to call unmanaged code with permission.Hello World!Called unmanaged code with permission.
安全性和性能
.NET Framework 安全系统可以防止从网络上下载的恶意代码损坏您的计算机系统。但即使代码从不引发 SecurityException,这些安全检查也并不是没有代价的。
通常,公共语言运行库验证非托管方法的调用方在运行时是否对每个非托管方法调用都具有非托管代码访问权限。这对多次调用非托管代码的应用程序来说代价可能是非常高的。SuppressUnmanagedCodeSecurityAttribute 可以更改此默认行为。当用该属性声明非托管方法时,将在调用该方法的代码被加载到公共语言运行库中时检查安全请求。
安全说明 使用 SuppressUnmanagedCodeSecurityAttribute 时,应格外小心以确保没有引入安全漏洞。例如,开发人员需要验证用户正在安全使用非托管 API 以及调用方无法影响或滥用此调用。开发人员还可以添加适当的请求以确保所有调用方都具有适当的权限。例如,如果对本机代码进行调用以访问文件(目的是利用结构化存储,如扩展文件属性等),而非托管代码请求被禁止,则应明确发出一个文件 IO 请求以确保代码不会被误用。
示例 3:优化非托管调用
在本例中,对非托管代码权限的检查仅在加载时执行一次,而不是在每次调用非托管方法时都执行。如果多次调用非托管方法,则可能极大地提高性能。
// SuppressSecurity.csusing System;using System.Security;using System.Security.Permissions;using System.Runtime.InteropServices;class NativeMethods{ // This is a call to unmanaged code. Executing this method requires // the UnmanagedCode security permission. Without this permission, // an attempt to call this method will throw a SecurityException: /* NOTE: The SuppressUnmanagedCodeSecurityAttribute disables the check for the UnmanagedCode permission at runtime. Be Careful! */ [SuppressUnmanagedCodeSecurityAttribute()] [DllImport("msvcrt.dll")] internal static extern int puts(string str); [SuppressUnmanagedCodeSecurityAttribute()] [DllImport("msvcrt.dll")] internal static extern int _flushall();}class MainClass{ // The security permission attached to this method will remove the // UnmanagedCode permission from the current set of permissions for // the duration of the call to this method. // Even though the CallUnmanagedCodeWithoutPermission method is // called from a stack frame that already calls // Assert for unmanaged code, you still cannot call native code. // Because this method is attached with the Deny permission for // unmanaged code, the permission gets overwritten. However, because // you are using SuppressUnmanagedCodeSecurityAttribute here, you can // still call the unmanaged methods successfully. // The code should use other security checks to ensure that you don't // incur a security hole. [SecurityPermission(SecurityAction.Deny, Flags = SecurityPermissionFlag.UnmanagedCode)] private static void CallUnmanagedCodeWithoutPermission() { try { // The UnmanagedCode security check is disbled on the call // below. However, the unmanaged call only displays UI. The // security will be ensured by only allowing the unmanaged // call if there is a UI permission. UIPermission uiPermission = new UIPermission(PermissionState.Unrestricted); uiPermission.Demand(); Console.WriteLine("Attempting to call unmanaged code without UnmanagedCode permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code without UnmanagedCode permission."); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code."); } } // The security permission attached to this method will add the // UnmanagedCode permission to the current set of permissions for the // duration of the call to this method. // Even though the CallUnmanagedCodeWithPermission method is called // from a stack frame that already calls // Deny for unmanaged code, it will not prevent you from calling // native code. Because this method is attached with the Assert // permission for unmanaged code, the permission gets overwritten. // Because you are using SuppressUnmanagedCodeSecurityAttribute here, // you can call the unmanaged methods successfully. // The SuppressUnmanagedCodeSecurityAttribute will let you succeed, // even if you don't have a permission. [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] private static void CallUnmanagedCodeWithPermission() { try { Console.WriteLine("Attempting to call unmanaged code with permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code with permission."); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code. Whoops!"); } } public static void Main() { SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); // The method itself is attached with the security permission Deny // for unmanaged code, which will override the Assert permission in // this stack frame. However, because you are using // SuppressUnmanagedCodeSecurityAttribute, you can still call the // unmanaged methods successfully. // The code should use other security checks to ensure that you // don't incur a security hole. perm.Assert(); CallUnmanagedCodeWithoutPermission(); // The method itself is attached with the security permission // Assert for unmanaged code, which will override the Deny // permission in this stack frame. Because you are using // SuppressUnmanagedCodeSecurityAttribute, you can call the // unmanaged methods successfully. // The SuppressUnmanagedCodeSecurityAttribute will let you succeed, // even if you don't have a permission. perm.Deny(); CallUnmanagedCodeWithPermission(); }}
输出
Attempting to call unmanaged code without UnmanagedCode permission.Hello World!Called unmanaged code without UnmanagedCode permission.Attempting to call unmanaged code with permission.Hello World!Called unmanaged code with permission.
代码讨论
请注意,以上示例允许两个非托管调用都成功,即使第一个调用不具有 UnmanagedCode 权限。在使用 SuppressUnmanagedCodeSecurityAttribute 时,应使用其他安全检查以确保不会招致安全漏洞。在上面的示例中,通过在非托管调用之前添加 UIPermission 的 Demand:
uiPermission.Demand();
做到这一点,它确保调用方具有显示 UI 的权限。
文章来源于领测软件测试网 https://www.ltesting.net/