Application Security

Lesson 1: CAS

Application Security

Code Access Security (CAS) enables you to control permissions individual applications have, e.g. If a friend send you a text editor you can restrict it to opening a window and prompting to open and save files. It won't be able to send e-mails, upload files to the web create files, etc. even if you are logged on as an administrator.

As a developer must write applications that work even with restricted permissions.

Understanding CAS

Permits system admins and developers to control application authorization similar to way users are authorized. Can allow one app to read and write the registry, while restricting access for another.

CAS can only be applied to managed apps using .NET framework. If CAS is used to restrict permissions of assembly, that assembly is considered partially trusted. Partially trusted assemblies must undergo CAS permission checks each time they access a protected resource. Some assemblies are exempt from CAS checks and are considered fully trusted. Fully trusted assemblies, like unmanaged code, can access any system resources the user has permissions to access.

Elements of CAS

Evidence

Every security system must identify participants to determine what they can and can't do. CAS identifies and assigns permissions to applications rather than people so it can't use user names, passwords, ACLs, etc. Instead it identifies assemblies using evidence.

Each piece of evidence is a way that an assembly can be identifier, such as its location, a hash of its code or the assemblies signature. The evidence determines which code group it belongs to. Code groups grant an assembly a permission set.

2 types of evidence:

  • Host Evidence - describes assembly origin, e.g. Application directory, URL, etc. Also describes the assemblies identity such as the hash, publisher or strong name.
  • Assembly Evidence - is customer user or developer evidence.
Permission

A Permission is a CAS Access Control Entry, e.g. File Dialog permission determines if assembly can prompt user with the Open dialog box, Save dialog box, both or neither.

By default 19 permissions available. Each corresponds to two members of System.Security.Permissions namespace - one for imperative and the other declarative use.

Can add custom permissions.

Permission Sets

A Permission Set is a CAS Access Control List, e.g. The Internet default permission set contains:

  • File Dialog
  • Isolated Storage File
  • Security
  • User Interface
  • Printing

The LocalIntranet contains the above and adds

  • Environment Variables
  • Reflection
  • DNS

based on theory that code running on a local network deserves more trust than code running from the Internet.

Framework includes seven default permission sets:

  1. FullTrust Exempt assembly from CAS permission checks
  2. SkipVerificationAllows assembly to bypass permission checks. Improves performance but sacrifices security.
  3. Execution Allows assembly to run, but grants no other permissions.
  4. Nothing Grants no permissions to assembly, not even allowed to run.
  5. LocalIntranet Grants generous set of permissions, including print and access to event log. Does not allow access to file system - except through Open and Save dialog boxes.
  6. Internet Grants restricted set of permissions. Even malicious assemblies should not bwe able to cause serious damage.
  7. Everything Grants assembly all permissions. Different from FullTrust which skips all CAS security checks.

Code Groups

Associate assemblies with permission sets. Provide similar service to CAS as user groups provide to RBS. Don't have to add assemblies individually to code group, instead membership is determined by evidence that is specified as the code groups membership condition.

For example any cod running from the Internet should be a member of the Internet code group.

It may seem limiting to specify a single type of evidence and single permission set for a code group. But as assembly can be a member of multiple code groups. It will receive the union of its code groups. Additionally can define nested groups and assign permissions only if assembly meets all evidence requirements of parent and child groups. Nesting allows permissions to be assigned based on assembly having more than one type of evidence, e.g. Microsoft_Strong_Name code groups within the My_Computer_Zone within All_Code group.

Security Policy

Logical grouping of code groups and permission sets. Can also contain custom assemblies that define other types of policies. Allow administrators to configure CAS settings at multiple levels. By default 4 configurable policy levels:

  1. Enterprise - highest level describing policy for entire enterprise. Often configured via Active Directory.
  2. Machine- Applies to all code run on a particular machine.
  3. User - Defines personal permission on a per user basis.
  4. Application Domain

The runtime evaluates the Enterprise, Machine and User policies and grants an assembly the minimum set of permissions granted by any of the levels (i.e. The intersection). By default Enterprise and User policies grant all code full trust which causes the Machine security policy alone to restrict CAS permissions.

CAS and Operating System Security

CAS is completely independent of OS security.

Use entirely different tools to administer CAS.

Can control a users or groups file permissions via Windows Explorer, have to use .NET Framework Configuration tool to grant or restrict assembly file permissions.

CAS works on top of existing OS security. When determining if assembly can take particular action both CAS and OS security are evaluated. The most restrictive set of permissions are applied, e.g. If CAS grants assembly write access to c:\windows, but user running that assembly does not have that permission then the assembly cannot write to that folder.

Configure CAS

When developing should adjust permission set assigned to My_Computer_Zone. By default it is set to Full Trust - thus ignoring any CAS statements in your applications. Change this to Everything, which has similar permissions but respects CAS statements.

.Net Framework Configuration tool is continent mechanism for controlling CAS. Can archive similar control using caspol.exe - a command line tool. Caspol has many parameters (see -? for a full set).

For example to grant an assembly full trust:

Caspol -addfulltrust c:\myassembly.exe

To change My_Conmputer_Zone policy to use Intranet permission set:

Caspol -chgroup My_Computer_Zone LocalIntranet

Lesson 2: Declarative Security

CAS restricts permissions granted to assembly. Must plan to have application executing in a partially trusted environment. Use declarative CAS demands to ensure application has all necessary permissions, also provides administrators with hint on what security application requires.

Reasons to use CAS Assembly Declarations
  • Ensure runtime does not run application without granting access to required resources. If application does not have exception handling to respond to situations where CAS permissions are missing then use SecurityAction.RequestMinimum to declare all CAS permissions required by application. If application is started without these required permissions the runtime throws an exception - better than having the application fail in unpredictable ways.
  • Create sandbox for application to prevent attackers from manipulating it to gain access to unintended resources. The principle of least privilege reduces chance of attacker abusing assembly.
  • Verify that application can run with limited CAS permissions and so run in partially trusted zones. Currently no easy way to identify permissions required by application. If develop application using SecurityAction.RequestOptional CAS declaration, the runtime grants assembly only those permissions you specify and throws an exception when additional ones are required.
Classes for CAS permissions

For each type of resource that can be protected, .NET provides a class, examples include:

  • AspNetHostingPermission - access resources in ASP.NET environments.
  • DnsPermission - access DNS.
  • OleDbPermission - access databases via OleDB.
  • RegistryPermission - access the registry.
  • WebPermission - make or accept connections on a web address.
  • and many, many more...

Each class has unique members to further control permissions, for example the OleDbPermission.AllowBlankPassword property controls if the assembly can use a blank password.

These classes derive from CodeAccessSecurityAttribute class and so share some common properties and methods. 2 of particular importance:

  • Action - specifies security action to take. Must set to one of 3 choices:
  • SecurityAction.RequestMinimum - requires a permission for assembly to run. If assembly lacks specified CAS permission the runtime throws an exception.
  • SecurityAction.RequestOptional - refuses all permissions not listed in SecurityAction.RequestOptional or SecurityAction.RequestMinimum declaration. If assembly lacks requested permission runtime does not throw an exception.
  • SecurityAction.RequestRefuse - ensure application does not have access to a critical resource that could be potentially abused. Does not cause a runtime exception.
  • Unrestricted - boolean which when true enabled access to all classes permissions
How to Create Assembly Declarations

For example, the following code requires access to the boot.ini file:

[assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum, Read=@"C:\boot.ini")]
assembly DeclarativeExampe
{
    class Program
    {
    }
}

This does not improve security because it does not restrict the assemblies permissions. To improve security use SecurityAction.RequestOptional or SecurityAction.RequestRefuse, for example to interact with the debugger (UIPermission), have full access to the c:\ drive (except for c:\windows) issue the following:

[assembly:UIPermission(SecurityAction.RequestMinimum, Unrestricted=true)]
[assembly:FileIOPermissionAttribute(SecurityAction.RequestOptional, Read=@"C:\")]
[assembly:FileIOPermissionAttribute(SecurityAction.RequestRefuse, Read=@"C:\Windows")]
assembly DeclarativeExampe
{
    class Program
    {
    }
}
Guidelines for assembly declarations
  • Use SecurityAction.RequestMinimum to require evey permission needed by assembly
  • Use SecuirytAction.RequestOptional to list every permission assembly uses. Declare most granular permissions possible - including specific files or registry keys that will be accessed
  • Use SecurityAction.RequestRefuse to further refine permissions listed in SecurityAction.RequestOptional

Lesson 3: Declarative and Imperative Security

CAS can be used declaratively (compiler performs security checks prior to running code) or imperatively (code performs security checks and controls what happens if check fails).

Types of Method Permission Requests

There are six options available for imperative and declarative permissions within a method.

  • Assert - instructs runtime to ignore the fact that caller might not have specified permissions. Assemblies must have AssertAnyPermissionThatHasBeenGranted security permission setting.
  • Demand - instructs runtime to throw exception if caller (and callers higher in stack) lack specified permission.
  • Deny - causes runtime to reduce methods access by removing specified permission.
  • InheritanceDemand - instructs runtime to throw exception if assembly inheriting from class lacks specified permission.
  • LinkDemand - Causes runtime to throw exception if immediate caller (but not those higher in stack) lacks specified permission.
  • PermitOnly - instructs runtime to reduce methods access by removing all permissions except for that specified.

Party analogy:

Host = a method, bounce = .NET framework, guests = calling assembly, invitation = CAS permission.

Host calls InvitedGuests.LinkDemand, bouncer checks invitation of first guest and then allows everyone else into the party - quick, but allows people to sneak in.

Host calls InvitedGuests.Demand, bouncer checks invitation of every guest - slow!

To speed up, first invited guest could use InvitedGuests.Assert to assure bouncer that all guests in group were invited - assuming bouncer trusted first guest enough.

If host wants to ensure people dance, host would use Dancing.PermitOnly to instruct bounder to make sure guests stayed on dance floor. If host wanted guests to do anything but dance, the host would use Dancing.Deny.

Guidelines for Method Permission Requests
  • Use SecurityAction.PermitOnly to limit permissions available to each method. List every permission method requires.
  • Use SecurityAction.Deny to further refine permissions available to method.
  • Use CodeAccessPermission.PermitOnly to reduce permissions when section of method requires fewer permissions than rest. Particularly important when calling 3

rd party objects.

  • Use CodeAccessPermssion.Assert to allow partially trusted code to call method that requires permissions caller might lack. Review code for security vulnerabilities - Assert can be abused by attacker to gain elevated privileges. Use CodeAccessPermission.RevertAssert to restore original permssions.
  • Use CodeAccessPermission.Demand only when your assembly implements customised functionality that does not rely on functionality built into .NET framework - e.g. Calls to unmanaged code.
Techniques for Demanding Permissions

Demand causes permission check to verify access of all callers

LinkDemand verifies only immediate caller - faster, but less secure.

Declaratively Demand CAS Permissions

Similar to creating CAS assembly declarations.

Create declarations as method attributes, rather than assembly attributes, for example:

[FileIOPermission(SecurityAction.Demand, Write=@"C:\Program Files\")]
public static void createProgramFolder() {}

If writing base classes, can restrict assemblies that may inherit using SecurityAction.InheriatnceDemand, e.g. Only assemblies signed with MyCertificate.cer can inherit from the following class:

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

Can use same syntax to protect individual class members from being overridden by derived class.

Imperatively Demand CAS Permissions
public static void CreateProgramFolder()
{
    FileIOPermission fp = new FileIOPermission(FileIOPermissionAccess.Write, @"C:\Program Files");
    fp.Demand();
}

The advantage of using imperative demands is that the security exceptions can be caught within the method and handled gracefully. If you just want to throw exception back to caller then use declarative demand.

Analyse granted permissions

Do not use Demand to check if assembly has particular CAS permission, it is designed to check assemblies caller for permission. To check the assembly itself use the System.Security.SecurityManager.IsGranted() method.

FileIOPermission filePermissions = new FileIOPermission(FileIOPermissionAccess.Read, @"C:\Windows\");

if (SecurityManager.IsGranted(filePermissions) == true)

...

Limiting Permissions

Use CAS declarations to restrict CAS permissions granted to assembly to bare minimum required for it to function. Can control permissions on more granular level by restricting permissions for individual methods using method declarations or imperative statements.

Use RequestRefuse and RequestOptional for assembly declarations.

Use Deny and PermitOnly for methods.

Declarative Limit

Following examples prevent a method accessing the c:\windows directory and to limit web requests to www.microsoft.com

[FileIOPermission(SecurityAction.Deny, ViewAndModify-@"C:\Windows")]

[WebPermission(SecurityAction.PermitOnly, ConnectPattern=@"http://www\.microsoft\.com/.*")]

Note, declarative security is static - if require dynamically generated file paths, web addresses, etc then must use imperative security.

Imperatively Limit

Same limits as before...

FileIOPermission fp = new FileIOPermission(FileIOPermissionAccess.AllowAccess, @"C:\windows")]

fp.Deny;

Regex pattern = new Regex(@"http://www\.microsoft\.com/.*");

WebPermission wp = new WebPermission(NetworkAccess.Connect, pattern);

wp.PermitOnly();

For parts of code that need a permission previoulsy blocked use the CodeAccessPermission.RevertDeny or CodeAccessPermission.RevertPermitOnly static methods.

Handling Errors

Use PermitOnly to limit permissions during error handling routines. Attackers often initiate error conditions and then abuse the handlers. Using PermitOnly to limit CAS permissions to bare minimum to log event reduces risk that error handling routine is abused.

try
{
    ...
}

catch
{
    EventLogPermission ePerm = new EventLogPermission(PermissionState.Unrestricted);

    ePerm.PermitOnly();
    // Log event
    CodeAccessPErmission.RevertPermitOnly();
}

Relaxing Permissions & Improving Performance

CAS can decrease performance. Demand is particularly costly as it forces runtime to check permission of every caller. LinkDemand is less costly. Assert causes the runtime to bypass security checks. The Assert method allows a method to vouch for all its callers.

To use Assert, assembly must have SecurityPermissionFlag.Assertion privilege - in .NET Framework Configuration Tool this is represented by "Assert Any Permission That Has Been Granted" item in security permissions dialogue box.

Assert allows assembly to vouch for security of lesser-privileged assemblies, for example can allow assembly in Internet zone to save file to users disk. Create assembly with AllowPartiallyTrustedCallersAttribute. Add a public method that writes a file. Create a FileIOPermission object and call its Assert method before writing the file. Assert can only be used once in a method, to assert multiple permissions use a permission set. Assert cannot override operating system role-based security.

Calling trusted code from partially trusted code

Usually partially trusted code cannot call string-named assemblies. Can override by adding AllowPartiallyTrustedCallersAttrribute to assembly. If assembly does not have a string name then it is accessible to partially trusted code.

Permission Sets

Collections of permissions that can be used imperatively, e.g.

PermissionSet ps = new PermissionSet(PermissionState.None);

ps.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, @"C:\Windows"));

ps.AddPermission(new FileIOPermission(FileIOPermissionAccess.Write. @"C:\InetPub"));

ps.Demand();

Download