Export (0) Print
Expand All

Security Tutorial

Visual Studio .NET 2003

This tutorial discusses .NET Framework security and shows two ways to modify security permissions in C#: imperative and declarative security.

Sample Files

See Security Sample to download and build the sample files discussed in this tutorial.

Further Reading

Tutorial

Most application and component developers should not need to do anything special in order to work with the .NET Framework security system and benefit from the safety protection it provides.

One exception that requires more in-depth knowledge and special consideration of the security system is secure libraries. This code represents the boundary between secure managed code and unrestricted code, such as native code (that is outside the ability of the .NET Framework security infrastructure to enforce). These libraries typically must be highly trusted to work, and are the one place in managed code where a programming error can potentially expose a security vulnerability. Code access security can't eliminate the potential for human error, but compared to the much larger volume of application code that uses a few secure libraries, the amount of code that requires close scrutiny is dramatically reduced.

Examples

The tutorial includes the following examples:

Security

The .NET Framework security protects your code and data from being misused or damaged by other code by enforcing security restrictions on managed code. When a .NET Framework application requests permission, the security policy established by the administrator grants the permission or refuses to run the code. Trust is based on evidence about the code such as a digital signature, where the code comes from, and so forth. Once granted, security enforces permissions that control what code is (and by not being granted, what code is not) allowed to do.

Permissions

The .NET Framework security allows code to use protected resources only if it has "permission" to do so. To express this, the .NET Framework uses the concept of permissions, which represent the right for code to access protected resources. Code requests the permissions it needs, and the security policy applied by the .NET Framework determines which permissions the code is actually granted.

The .NET Framework provides code access permission classes, each of which encapsulates the ability to access a particular resource. You use these permissions to indicate to the .NET Framework what your code needs to be allowed to do and to indicate what your code's callers must be authorized to do. Policy also uses these objects to determine what permissions to grant to code.

Policy

Enforcement of security policy is what makes .NET Framework managed code safe. Every assembly that loads is subject to security policy that grants code permissions based on trust, with trust based on evidence about the code. See the .NET Framework documentation in the Reading List for information on administering security policy.

Demanding Security Permissions in C#

There are two ways to demand security permissions in C#:

  • Imperatively: Using calls to permission classes in the .NET Framework
  • Declaratively: Using security permission attributes

The following two examples demonstrate these two approaches. For more information on demanding security permissions, see Demands.

Example 1: Imperative Security

The following is an example of using .NET Framework calls to deny the UnmanagedCode permission.

// ImperativeSecurity.cs
using 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();
    }
}

Output

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.

Example 2: Declarative Security

This is the same example using attributes for the security permissions.

// DeclarativeSecurity.cs
using 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();
    }
}

Output

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.

Security and Performance

The .NET Framework security system prevents malicious code downloaded from the network from damaging your computer system. However, these security checks are not without cost, even if your code never throws a SecurityException.

Normally the common language runtime verifies that the caller of an unmanaged method has unmanaged code access permission at run time for every call to the unmanaged method. This can be very expensive for applications that make many calls to unmanaged code. The SuppressUnmanagedCodeSecurityAttribute changes this default behavior. When an unmanaged method is declared with this attribute, the security demand is checked when code that calls the method is loaded into the common language runtime.

Security Note   When using the SuppressUnmanagedCodeSecurityAttribute, you should take extra care to ensure that you are not introducing a security hole. For example, the developer needs to verify that he/she is using the unmanaged API safely and that callers cannot influence or abuse the call. Alternatively, the developer can add an appropriate demand to ensure that all callers have the appropriate permissions. For example, if a call was made into native code to access a file (to take advantage of structured storage such as extended file properties, and so forth) and the unmanaged code demand was suppressed, then a file IO demand should be made explicitly to ensure that the code cannot be misused.

Example 3: Optimizing Unmanaged Calls

In this example, the check for the unmanaged code permission is executed once at load time, rather than upon every call to the unmanaged method. If the unmanaged method is called multiple times, this could yield a significant performance gain.

// SuppressSecurity.cs
using 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();
    }
}

Output

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.

Code Discussion

Note that the above example allows both unmanaged calls to succeed even though the first call doesn't have UnmanagedCode permission. When using the SuppressUnmanagedCodeSecurityAttribute, you should use other security checks to ensure that you don't incur a security hole. In the above example, this is done by adding the Demand for the UIPermission:

uiPermission.Demand();

before the unmanaged call, which ensures that the caller has permission to display UI.

See Also

C# Tutorials

Show:
© 2014 Microsoft