Introducing Code Access Security in .NET Framework

Last Updated: Nov 02, 2025
8 min read
Legacy Archive
Legacy Guidance: This article preserves historical web development content. For modern .NET 8+ best practices, visit our Tutorials section.

Malicious codes, both from known and unknown sources, are a threat to any computer that has internet access. You're vulnerable to getting malicious code through email, when you download documents, or even when you're just surfing the internet. Though the secured system of assigning a username and password for accessing resources seems safe, many times this method has proved faulty and not secure, as hackers can obtain usernames and passwords without even the knowledge of legitimate users.

Subsequently, the username and password can be used either to tamper with data or to steal valuable information. To avoid possible security breaches, there's a system called Code Access Security in the .NET Framework that can protect you from any malicious or harmful codes getting executed in your computer system and save you from any security compromise.

Core Principles of Code Access Security

Using Code Access Security, you can reduce your exposure to vulnerable situations, as the system employs a unique method of rights involving execution of codes at varied levels and with varied identity levels. Code Access Security in the .NET Framework works on the following principles:

Granting Permissions and Rights

Code Access Security grants permissions and rights to access various resources, including access to the computer itself. The system uses a well-defined security policy that can be configured or customized to meet your requirements. Various permission levels can be assigned to sets of code, thereby maintaining a well-secured configuration.

Declarative Permission Example
using System;
using System.Security.Permissions;

class SecureFileAccess
{
    // Declarative security - permission specified at compile time
    [FileIOPermission(SecurityAction.Demand, 
        Read = @"C:\SecureData\")]
    public void ReadSecureFile()
    {
        // Code only executes if caller has file read permission
        Console.WriteLine("Reading secure file");
    }
    
    // Request minimum permissions
    [FileIOPermission(SecurityAction.RequestMinimum, 
        Read = @"C:\Config\")]
    public void ReadConfig()
    {
        Console.WriteLine("Reading configuration");
    }
}

Permission-Based Execution

When important or sensitive sets of code need to be executed, the system will ask for permission from an authorized official. Only a person who holds a specific permission or role can run the codes to get desired results. Various levels of permissions can be granted to various persons using the system, and these can be customized based on the prevailing security policy of the organization.

Imperative Permission Check
using System;
using System.Security;
using System.Security.Permissions;

class DatabaseAccess
{
    public void AccessDatabase(string connectionString)
    {
        // Imperative security - check at runtime
        FileIOPermission permission = new FileIOPermission(
            FileIOPermissionAccess.Read, 
            @"C:\Database\");
        
        try
        {
            // Demand permission before proceeding
            permission.Demand();
            Console.WriteLine("Permission granted. Accessing database.");
            // Database access code here
        }
        catch (SecurityException ex)
        {
            Console.WriteLine("Access denied: " + ex.Message);
        }
    }
}

Code-Level Restrictions

A provision is made available where you can configure your system to ask for specific permission whenever code is executed. Various levels of restriction can be introduced at code level itself that makes the system cross-check whether the person permitting the running of code is really authorized to do so or not.

Permission Sets
using System;
using System.Security;
using System.Security.Permissions;

class RestrictedOperations
{
    // Deny specific permissions
    [SecurityPermission(SecurityAction.Deny, 
        UnmanagedCode = true)]
    public void SafeOperation()
    {
        // This method cannot call unmanaged code
        Console.WriteLine("Executing safe operation");
    }
    
    // Assert permissions for trusted code
    [FileIOPermission(SecurityAction.Assert, 
        Write = @"C:\Logs\")]
    public void WriteLog(string message)
    {
        // Assert allows this code to write logs
        // even if caller doesn't have permission
        Console.WriteLine("Writing to log: " + message);
    }
}

Writing Type-Safe Code

With type-safe code, the original source code can be secured, and you can make only a compiled runtime version capable of running in a real-time environment. Type-safe code cannot access memory locations it's not authorized to access, preventing buffer overruns and memory corruption that malicious code might attempt.

Type-Safe Code Example
// Type-safe code prevents memory corruption
class TypeSafeExample
{
    private string[] data = new string[10];
    
    public void StoreData(int index, string value)
    {
        // Type safety ensures index is valid
        if (index >= 0 && index < data.Length)
        {
            data[index] = value;
        }
        else
        {
            throw new IndexOutOfRangeException();
        }
        
        // Cannot access arbitrary memory addresses
        // Cannot bypass security through pointer manipulation
    }
}

Imperative and Declarative Syntax

You can make use of declarative and imperative calls and customize your triggers. The triggers can be made to execute a set of predefined codes, but only with correct permission levels. With such a facility, you can fully secure your data and system from external threats.

Declarative security uses attributes to specify security requirements at compile time. Imperative security uses code statements to check permissions at runtime, allowing for dynamic security decisions based on conditions.

Combining Both Approaches
using System;
using System.Security.Permissions;

class SecurityExample
{
    // Declarative - fixed at compile time
    [FileIOPermission(SecurityAction.Demand, 
        Read = @"C:\Public\")]
    public void ReadPublicFile()
    {
        Console.WriteLine("Reading public file");
    }
    
    // Imperative - dynamic at runtime
    public void ReadFile(string path, bool isSecure)
    {
        if (isSecure)
        {
            FileIOPermission perm = new FileIOPermission(
                FileIOPermissionAccess.Read, 
                @"C:\Secure\");
            perm.Demand();
        }
        
        Console.WriteLine("Reading file from: " + path);
    }
}

Secure Class Libraries

With the help of class libraries, you can customize and specify your various permission levels required for access to data and code. You'll be able to employ permissions globally in the entire system. This creates a consistent security model across your application.

Custom Permission Class
using System;
using System.Security;
using System.Security.Permissions;

// Custom security manager class
class SecurityManager
{
    public static void CheckDatabaseAccess()
    {
        // Create permission set
        PermissionSet permissions = new PermissionSet(
            PermissionState.None);
        
        // Add specific permissions
        permissions.AddPermission(
            new FileIOPermission(
                FileIOPermissionAccess.Read, 
                @"C:\Database\"));
        
        permissions.AddPermission(
            new SecurityPermission(
                SecurityPermissionFlag.Execution));
        
        // Demand all permissions in set
        permissions.Demand();
    }
    
    public static bool HasPermission(IPermission permission)
    {
        try
        {
            permission.Demand();
            return true;
        }
        catch (SecurityException)
        {
            return false;
        }
    }
}

Best Practices

You can make use of Code Access Security concepts to develop applications for any real-time runtime environment. Write type-safe code to protect your source code and ensure only compiled runtime versions can execute. Use declarative security for fixed requirements and imperative security for dynamic decisions.

Configure your application to evaluate permission levels and ensure your codes run only by authorized persons. Use secure class libraries to customize and specify permission levels globally. These practices help build robust security into your applications from the ground up.

FAQ

What's the difference between imperative and declarative security?

Declarative security uses attributes on classes and methods to specify permissions at compile time. Imperative security uses code statements to check permissions at runtime. Use declarative for fixed requirements and imperative for dynamic security decisions based on runtime conditions.

How does evidence-based security work?

Evidence-based security evaluates code based on its origin, such as website URL, digital signature, or installation location. The security policy uses this evidence to assign permissions, allowing you to trust code from known sources while restricting code from untrusted sources.

Why is type-safe code important for security?

Type-safe code cannot access memory locations it doesn't have permission to access, preventing buffer overruns and memory corruption attacks. This protects your source code and ensures malicious code cannot bypass security checks through memory manipulation.

Back to Articles