Skip to main content

.NET Code Access Security

Introduction to Code Access Security

Code Access Security (CAS) is an essential part of the .NET Framework security model, designed to help protect computer systems from malicious code and to help ensure that code has the appropriate permissions to access resources. Unlike role-based security that determines what a user can do, CAS determines what your code can do, regardless of the user running it.

While CAS has been partially deprecated in modern .NET versions in favor of other security mechanisms, understanding its concepts remains valuable for .NET developers, especially when working with legacy systems or when implementing custom security solutions.

Key Concepts of Code Access Security

1. Evidence-based Security

In the CAS model, assemblies are granted permissions based on "evidence" about the code, such as:

  • Where the code comes from (URL, site, zone)
  • Digital signatures and certificates
  • Strong name identities
  • Custom evidence specific to your application

2. Permissions and Permission Sets

Permissions represent access to a protected resource or the ability to perform an operation:

  • FileIOPermission: Controls access to the file system
  • EnvironmentPermission: Controls access to system environment variables
  • SecurityPermission: Controls access to security-sensitive operations
  • ReflectionPermission: Controls access to metadata through reflection
  • UIPermission: Controls access to user interface functionality

Permission sets are named collections of permissions that can be granted to assemblies.

3. Code Groups

Code groups are categories of code that share a common security requirement. The runtime associates permissions with assemblies based on which code groups they belong to.

4. Security Policy

Security policy is the set of rules that determine which permissions are granted to which assemblies.

Working with Code Access Security

Demanding Permissions

When you want to ensure that all callers in the call stack have a specific permission, you can use permission demands:

csharp
// Demand that all callers have file IO permissions
FileIOPermission filePermission = new FileIOPermission(PermissionState.Unrestricted);
filePermission.Demand();

// Now proceed with file operations

Permission Requests

Your assembly can explicitly request permissions it needs:

csharp
[assembly: FileIOPermission(SecurityAction.RequestMinimum, 
Read = @"C:\Data\")]

This code specifies that the assembly needs at minimum read access to the C:\Data\ directory.

Example: Writing a Security-Aware Component

Here's an example of a class that performs file operations with appropriate security checks:

csharp
using System;
using System.IO;
using System.Security;
using System.Security.Permissions;

public class SecureFileReader
{
public string ReadFile(string path)
{
// First, demand permission to read files
FileIOPermission permission = new FileIOPermission(FileIOPermissionAccess.Read, path);

try
{
permission.Demand();

// If we get here, we have permission to read the file
return File.ReadAllText(path);
}
catch (SecurityException ex)
{
Console.WriteLine("Security violation: " + ex.Message);
return null;
}
}
}

Using the SecureFileReader Component

csharp
public static void Main(string[] args)
{
SecureFileReader reader = new SecureFileReader();

try
{
// This will succeed if the code has permission
string content = reader.ReadFile(@"C:\public\document.txt");
Console.WriteLine("File content: " + content);

// This might fail if the code doesn't have permission
string restrictedContent = reader.ReadFile(@"C:\restricted\sensitive.txt");
Console.WriteLine("Restricted content: " + restrictedContent);
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}

Output when running with full trust:

File content: This is a public document.
Restricted content: This is sensitive information.

Output when running with restricted permissions:

File content: This is a public document.
Security violation: Request for the permission of type 'System.Security.Permissions.FileIOPermission' failed.

Advanced CAS Techniques

Assert, Deny, and PermitOnly

These are advanced mechanisms to control the permission stack:

csharp
public void WriteLogWithAssert(string message)
{
// Create a new permission for writing to the log file
FileIOPermission permission = new FileIOPermission(FileIOPermissionAccess.Write, @"C:\Logs\app.log");

try
{
// Assert the permission - stops the stack walk at this point
permission.Assert();

// Write to the log file even if callers don't have permission
File.AppendAllText(@"C:\Logs\app.log", message + Environment.NewLine);
}
finally
{
// Always undo the Assert when done
CodeAccessPermission.RevertAssert();
}
}

Custom Permissions

You can create custom permission types by extending CodeAccessPermission:

csharp
[Serializable]
public class DatabasePermission : CodeAccessPermission, IUnrestrictedPermission
{
private bool _unrestricted;
private string _connectionString;

// Constructor
public DatabasePermission(string connectionString)
{
_connectionString = connectionString;
_unrestricted = false;
}

// Implement required methods like Copy, Intersect, Union, etc.

public override IPermission Copy()
{
DatabasePermission copy = new DatabasePermission(_connectionString);
copy._unrestricted = _unrestricted;
return copy;
}

// Method to check if this permission is a subset of another
public override bool IsSubsetOf(IPermission target)
{
// Implementation depends on your permission logic
return true; // Simplified for this example
}

// Other required implementations...
}

Real-World Applications

Securing a Plugin System

Consider a plugin architecture where third-party developers can create plugins for your application:

csharp
public class PluginHost
{
public void LoadAndRunPlugin(string pluginPath)
{
// Create a sandbox with limited permissions
PermissionSet sandbox = new PermissionSet(PermissionState.None);

// Allow execution but restrict what the plugin can do
sandbox.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
sandbox.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, pluginPath));

// Load the plugin in the sandbox
AppDomain pluginDomain = AppDomain.CreateDomain("PluginSandbox");

try
{
// Set the permissions for the AppDomain
pluginDomain.SetAppDomainPolicy(SecurityManager.GetStandardSandbox(sandbox));

// Load and execute the plugin
pluginDomain.ExecuteAssembly(pluginPath);
}
finally
{
// Clean up
AppDomain.Unload(pluginDomain);
}
}
}

Partial Trust Web Applications

In classic ASP.NET applications, CAS was used to run web applications with reduced permissions:

csharp
// In web.config
<trust level="Medium" originUrl="" />

With this configuration, the web application would run with a predefined set of permissions that prevented it from accessing certain resources or performing certain operations, adding a layer of protection against security vulnerabilities.

Modern Alternatives to CAS

In newer versions of .NET, especially .NET Core and .NET 5+, CAS has been largely replaced by:

  1. Operating System Security: Relying on the security provided by the operating system.
  2. Sandboxing: Running untrusted code in separate processes or containers.
  3. WASM: Using technologies like WebAssembly for sandboxed execution.
  4. Security Boundaries: Establishing clear security boundaries between components.

Summary

Code Access Security is a security mechanism in the .NET Framework that helps control what resources and operations your code can access, regardless of the user running it. By using permissions, permission sets, and security policies, you can create applications that are more resilient against security threats.

While CAS has been partially deprecated in modern .NET, its concepts remain valuable for understanding security in .NET applications, especially in legacy systems or when building custom security solutions.

Additional Resources

  1. Microsoft Docs: Code Access Security
  2. .NET Security Programming
  3. Security in .NET Core

Exercises

  1. Create a simple console application that reads a file and implements proper permission checks using Code Access Security.

  2. Create a custom permission class that represents access to a specific database and implement the required methods.

  3. Implement a simple plugin system that loads assemblies with restricted permissions to prevent them from accessing sensitive resources.

  4. Research how you might implement similar security boundaries in a modern .NET Core application without using CAS.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)