Declarative vs. Imperative Security to Protect C# Methods

When Code Access Security is used declaratively compiler checks code for security exceptions and if Code Access Security is used imperatively code itself checks for security exceptions.

CAS has six additional options for imperative and declarative permissions.

SecurityAction.Assert – tells the runtime ignore any required permissions.

SecurityAction.Demand – tells the runtime to throw exception if caller lacks permission specified.

SecurityAction.Deny – reduces method access by removing existing permissions from the caller or caller higher in the stack.

SecurityAction.InheritanceDemand – throws exception if assembly which inherits from the class that does not have permissions.

SecurityAction.LinkDemand – throws exception if caller but not caller higher in the stack lacks permission. Requires immediate caller to have permissions.

SecurityAction.PermitOnly – removes all permissions except those specified.

Good example: “To understand each of these methods, consider a group of four guests who want to enter an exclusive party. The host (your method) has hired a bouncer (the .NET Framework runtime) to make sure that only guests (calling assemblies) with an invitation (a CAS permission) are allowed to enter the party (call your method).”

Declarative Demand

//Access files
[FileIOPermission(SecurityAction.Demand, Write = @"C:\Program Files\")]
public static void ProgramFolder()
{
    // Method
}

[WebPermission(SecurityAction.Demand, ConnectPattern = @"http://www\.microsoft\.com/.*")]
public static void WebPage()
{
     // Method
}

[PublisherIdentityPermission(SecurityAction.InheritanceDemand, CertFile = @"C:\Certificates\MyCertificate.cer")]
public class ProtectedInheritance
{
     // Class
}

Imperative Demand

public static void createProgramFolder()
{
    try
    {
        FileIOPermission myFilePermissions = new FileIOPermission(FileIOPermissionAccess.Write, @"C:\Program Files\");
        myFilePermissions.Demand();
        // Method
    }
    catch // Error-handling
    {}
}
public static void requestWebPage()
{
    try
    {
        Regex myPattern = new Regex(@"http://www\.site\.com/.*");
        WebPermission myPermissions = new WebPermission(NetworkAccess.Connect, myPattern);
        myPermissions.Demand();
        // Method logic
    }
    catch // Error-handling logic
    {}
}

We can run analysis of the method for security permissions

FileIOPermission filePermissions = new FileIOPermission(FileIOPermissionAccess.Read, @"C:\Windows\");
if (SecurityManager.IsGranted(filePermissions) == true)
   // Can Read C:\Windows directory
else
  
// Cannot C:\Windows directory

We can also limit permissions declaratively

[FileIOPermission(SecurityAction.Deny, ViewAndModify = @"C:\Windows\")]
[WebPermission(SecurityAction.PermitOnly, ConnectPattern = @"http://www\.site\.com/.*")]

Or we can limit permission imperatively

FileIOPermission filePermissions = new FileIOPermission(FileIOPermissionAccess.AllAccess, @"C:\Windows\");
filePermissions.Deny();
// Method

Regex connectPattern = new Regex(@"http://www\.site\.com/.*");
WebPermission webPermissions = new WebPermission(NetworkAccess.Connect, connectPattern);
webPermissions.PermitOnly();
// Method

We also need to keep in mind that any exception thrown can be used to bypass security as a result we must handle exception in a secure way just like regular method.

// C#
try
{
    // Assembly
}
catch
{
    EventLogPermission myErrorPerms = new EventLogPermission(PermissionState.Unrestricted);
    myErrorPerms.PermitOnly();
    // Log
    CodeAccessPermission.RevertPermitOnly();
}

We can always relax permission by calling on Assert method that tells runtime to bypass security. This is important to keep in mind if we decide to improve performance over resource consumption that is high during every check for the security permissions. In order to use Assert, the assembly must have the SecurityPermissionFlag.Assertion privilege. Limitation with Assert is that you can use it only once per method and it does not overwrite OS level permissions. However you can use multiple Asserts in permission set and then you can use it.

[FileIOPermission(SecurityAction.Assert, ViewAndModify = @"C:\Windows\")]
[WebPermission(SecurityAction.Assert, ConnectPattern = @"http://www\.site\.com/.*")]

Partially trusted code cannot access strong-named assemblies but we can bypass it using

[assembly:AllowPartiallyTrustedCallers]

Permission Set can be used same way as permission imperatively and we should use System.Security.Permissions.PermissionSet class to create permission set and then use AddPermission to specify permissions such as Assert, Demand, Deny, PermitOnly.

PermissionSet perms = new PermissionSet(PermissionState.None);
perms.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, @"C:\Windows"));
perms.AddPermission(new FileIOPermission(FileIOPermissionAccess.Write, @"C:\Inetpub"));
perms.AddPermission(new RegistryPermission(RegistryPermissionAccess.Write, @"HKEY_LOCAL_MACHINE\Software"));

perms.Demand();

We can also use RevertXXX to associate with permission class in order to remove associated permissons and denials. For instance RevertAssert removes all permission assertionts, RemoveAll removes all permissions.