Using Declarative and Imperative Security to Protect Methods

CAS can be used either declaratively, in which case the compiler performs security checks prior to running code, or imperatively, in which case the code itself performs security checks and controls what happens if the check fails. Aparte de utilizar las declaraciones CAS para proteger assemblies totalmente o de manera completa, you can also use CAS to declaratively protect individual methods within an assembly or use CAS to imperatively protect sections of code within a method. In this lesson you’ll learn how and why to use both imperative and declarative CAS demands to protect code within an assembly.

Types of Method Permission Requests

Although there are only three types of CAS assembly declarations (RequestOptional, RequestMinimum, and RequestRefuse), you have six options available for imperative and declarative permissions within a method. The following list describes each option:

  • Assert: Instructs the runtime to ignore the fact that callers might not have the specified permission. Assemblies must have the Assert Any Permission That Has Been Granted security permission setting.
  • Demand: Instructs the runtime to throw an exception if the caller and all callers higher in the stack lack the specified permission.
  • Deny: Causes the runtime to reduce the method’s access by removing the specified permission.
  • InheritanceDemand: Instructs the runtime to throw an exception if the assembly inheriting from the class lacks the specified permission.
  • LinkDemand: Causes the runtime to throw an exception if the immediate caller, but not callers higher in the stack, lack the specified permission.
  • PermitOnly: Instructs the runtime to reduce the method’s access by removing all permissions except for the specified permission.

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).

If the host calls InvitedGuests.LinkDemand, the bouncer will check the invitation of the first guest and then allow everyone else into the party. This is quick, but it might let people sneak into the party. If the host calls InvitedGuests.Demand, the bouncer will check the invitation of every guest individually. This process takes more time, but it ensures that nobody can sneak in.

To speed up the process of checking invitations, the first invited guests might use InvitedGuests.Assert to assure the bouncer that all the guests in the group were invited—assuming that the bouncer trusted the first guest enough. This procedure would also allow the first guest to bring guests who lacked invitations, which might be a good thing if the host wanted to have a lot of people at the party but didn’t want to hand out too many invitations (that might fall into the wrong hands).

However, it might be a bad thing if a thief discovered that he could sneak into the party. If the host wanted to ensure that people danced at the party (and never did anything else), the host would use Dancing.PermitOnly to instruct the bouncer to make sure that guests stayed on the dance floor. If the host wanted people to do anything but dance, the host would use Dancing.Deny to prevent anyone from dancing.

Guidelines for Using Method Permission Requests

Como desarrollador, usted tiene muchas opciones para la aplicación de CAS en sus aplicaciones. Elegir cómo implementar CAS para una situación particular puede ser complicado, sin embargo. Follow these guidelines to choose which CAS methods to use:

  • Use SecurityAction.PermitOnly declarations to limit the permissions available to each method. List every permission the method requires.
  • Use SecurityAction.Deny declarations to further refine the permissions available to each method
  • Use CodeAccessPermission.PermitOnly to imperatively reduce permissions when a section of a method requires fewer permissions than the rest of the method. This is particularly important when calling objects created by third parties. Use CodeAccessPermission.RevertPermitOnly to restore the permission.
  • Use CodeAccessPermission.Assert when you want to allow partially trusted code to call a method that requires permissions the caller might lack. Review your code carefully for potential security vulnerabilities; Assert can be abused by an attacker to gain elevated privileges. After you perform the functions requiring elevated privileges, use CodeAccessPermission.RevertAssert to restore the original permissions.
  • Use CodeAccessPermission.Demand only when your assembly implements customized functionality that does not rely on functionality built into the .NET Framework, such as calls to unmanaged code.

NOTE: Security risks of declarative demands

There’s a school of thought that says declarative security demands are less secure than imperative security demands because declarative demands can reveal to attackers too much about the code’s design and potential vulnerabilities. It’s true that declarative security demands are a bit easier for an attacker to analyze, but a sophisticated attacker could also examine imperative demands by using a tool that analyzes your assembly’s Intermediate Language (IL) code. It’s a bit harder for the attacker to analyze IL than to analyze the declarative security demands, but it wouldn’t make much of a difference to an attacker who was sophisticated enough to make use of security demand information. Also, declarative demands are faster than imperative demands.

Techniques for Demanding Permissions

Two of the SecurityAction enumerations and two of the CodeAccessPermission methods cause the runtime to throw an exception if the specified CAS permission is missing: Demand and LinkDemand. The difference between the two enumerations and methods is that Demand causes the permission check to verify the access of all callers, whereas LinkDemand verifies only the immediate caller.

To understand the difference, compare the Demand process demonstrated in Figure 11-9 with the LinkDemand process demonstrated in Figure 11-10. As you can see, Demand will detect whether any caller lacks the demanded permission or permission set, and will throw an exception. This is more secure than using LinkDemand, which checks only the immediate caller. However, as with almost every security mechanism, there is a trade-off. Demand requires the runtime to do more checks, which requires more processing time and slows performance. Using LinkDemand improves performance but increases the risk of an attacker successfully bypassing the check.

Demanding Permission 1.

IMPORTANT Demand and LinkDemand check the caller

Demand and LinkDemand do not check the current method’s permissions—they check the caller. However, if your assembly calls a private method that uses Demand or LinkDemand, the runtime will check your assembly’s permission because in this case your assembly is the caller.

LinkDemand Permission

Creating CAS method declarations is very similar to creating CAS assembly declarations. However, you must create the declarations as attributes to the method instead of to the assembly and you must use different SecurityAction enumerations. To create a declarative request, use one of the classes discussed in Lesson 2 of this chapter with the SecurityAction.Demand or SecurityAction.LinkDemand enumerations. The following sample shows two methods that use FileIOPermissionAttribute (in System.Security .Permissions) and WebPermissionAttribute (in System.Net) classes to declaratively verify that callers of particular methods have access to specific files and the www.microsoft.com Web site.

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

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

If you write classes from which other developers will derive, you can restrict which assemblies can inherit from your classes using the SecurityAction.InheritanceDemand enumeration. For example, only assemblies signed with the C:\Certificates\MyCertificate.cer certificate could inherit from the following class:

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

You can use the same declarative syntax to protect individual class members from being overridden by a derived class. This approach is necessary only when you want to provide levels of protection for individual members that are higher than those for the base class.

How to Imperatively Demand CAS Permissions

For each of the SecurityAction enumerations used to specify CAS declarations, there is a CodeAccessPermission method with the same name and function used for imperative permissions. You will use the SecurityAction enumerations for declarative security and the CodeAccessPermission methods for imperative security. The following sample performs the same checks as the sample code that used declarative CAS demands, but it performs the check imperatively:

public static void createProgramFolder()
{
       try
       {

              FileIOPermission filePermissions =new FileIOPermission(FileIOPermissionAccess.Write, @"C:\Program Files\");
              filePermissions.Demand();
             // Method logic
        }
        catch
        {
             // Error-handling logic
        }
}

public static void requestWebPage()

       try
       {
              Regex connectPattern = new Regex(@"
http://www\.microsoft\.com/.*");
              WebPermission webPermissions = new WebPermission(NetworkAccess.Connect, connectPattern);
              webPermissions.Demand();
             // Method logic
       }
       catch
       {
              // Error-handling logic
       }
}

Recuerda, la ventaja de utilizar las exigencias imperativas es que se puede capturar la excepción de seguridad dentro de su método y tratarla correctamente. Si desea generar una excepción de vuelta al autor de la llamada, utilice una demanda declarativa.

How to Analyze Granted Permissions

If you need to determine whether your assembly has a particular CAS permission, don’t use Demand. Demand is designed to check an assembly’s caller for permission, not the assembly itself. Instead, use the System.Security.SecurityManager.IsGranted method, as demonstrated by the following code sample:

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

        if ( SecurityManager.IsGranted(filePermissions) == true )
            // Assembly can read the C:\Windows directory
        else
            // Assembly cannot read the C:\Windows directory

BEST PRACTICES: Avoid redundant demands

Most classes in the .NET Framework use demands to ensure that callers have the permissions required to use them, so also calling Demand is redundant. For example, if you’re reading a line from a text file using a StreamWriter object, the object itself will demand FileIOPermission. Generally, use demands to protect custom resources that require custom permissions.

Techniques for Limiting Permissions

Always use CAS assembly declarations to restrict the CAS permissions granted to your assembly so that your assembly has only the bare minimum required for all functionality. You can control permissions on a more granular level by restricting permissions for individual methods using method declarations or by restricting permissions within methods using imperative statements.

Two of the SecurityAction enumerations and permission methods cause the runtime to reduce CAS permissions: Deny and PermitOnly. The difference between the two enumerations is that Deny removes a single permission or permission set, whereas PermitOnly removes all permissions except the requested permission or permission set. Recall from Lesson 2 that Deny performs a similar function to RequestRefuse, whereas PermitOnly is similar to RequestOptional.

How to Declaratively Limit Method Permissions
The following two declarations demonstrate how to prevent a method from accessing the C:\Windows\ directory and how to limit outgoing Web requests to only
www.microsoft.com:

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

NOTE: Limitations of declarative security

Declarative security criteria must be static. If you need to dynamically generate file paths, Web addresses, or any other aspects of the security criteria, you must enforce the security limitations imperatively.

How to Imperatively Limit Permissions

The following sample forces the same limitations as the sample code that used declarative CAS demands, but it limits the permissions imperatively:

// Deny access to the Windows directory
FileIOPermission filePermissions = new FileIOPermission(FileIOPermissionAccess.AllAccess, @"C:\Windows\");
filePermissions.Deny();
// Method logic
// Permit only Web access, and limit it to
www.microsoft.com
Regex connectPattern = new Regex(@"
http://www\.microsoft\.com/.*");
WebPermission webPermissions = new WebPermission(NetworkAccess.Connect, connectPattern);
webPermissions.PermitOnly();
// Method logic

If part of your code needs to use a permission that you previously blocked with Deny or PermitOnly, use the System.Security.CodeAccessPermission.RevertDeny or System.Security.CodeAccessPermission.RevertPermitOnly static methods to reenable the permission.

Best Practice for Handling Errors

Use PermitOnly to limit permissions during error-handling routines. Attackers often initiate an error condition in an application and then abuse that error condition to perform tasks that would not be possible under normal circumstances.

Using PermitOnly to limit CAS permissions to the bare minimum required to log the event and report an error to the user significantly reduces the risk that your error-handling routine can be abused. If your application will continue running after the error, be sure to revert to your original permissions—otherwise, normal application functionality will not be available.

For example, the following code catches an exception, restricts CAS permissions to those required to add events, and then reverts to the previous permission set:

try
{
        // Assembly logic
}
catch
{
          EventLogPermission errorPerms = new EventLogPermission(PermissionState.Unrestricted);
          errorPerms.PermitOnly();
          // Log event
          CodeAccessPermission.RevertPermitOnly();
}

Restricting permissions to those required for a specific block of code is an excellent example of following the principle of least privilege. Although it’s particularly important during error-catching routines, you can use this technique to limit the permissions of any block of code.

How to Relax Permissions and Potentially Improve Performance

Using CAS demands improves the security of an assembly but can decrease performance. In particular, calling a permission’s Demand method is costly because it forces the runtime to systematically check the permission of every caller. LinkDemand, discussed earlier, is one way to improve upon the performance of the Demand method, but it sacrifices some level of security. Another technique is the Assert method, which causes the runtime to bypass any security checks.

IMPORTANT: Compared with assert in C++
CodeAccessPermission.Assert is nothing like the assert function in C or C++.

Permission objects include the Assert method to enable a method to vouch for all callers. Figure 11-11 shows how a call to Assert stops the runtime from checking the CAS permissions of assemblies higher in the stack. This has two effects: improving performance by reducing the number of permission checks and allowing underprivileged code to call methods with higher CAS permission requirements.

Assert blocks demand checks

For example, if you create a RegistryPermission object and call the Assert method, your assembly must be granted RegistryPermission, but any code calling your assembly does not require the permission. If you call another method that uses Demand to require RegistryPermission, Demand will succeed whether or not your caller has been granted RegistryPermission.

You can use Assert either declaratively or imperatively, and the syntax is identical to other types of CAS declarations. The following example asserts permissions declaratively:

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

Although the following example asserts permissions imperatively:

// Block all CAS permission checks for file access to the Windows directory
FileIOPermission filePermissions = new FileIOPermission(FileIOPermissionAccess.AllAccess, @"C:\Windows\");
filePermissions.Assert();
// Method logic
// Block all CAS permission checks for Web access to
www.microsoft.com
Regex connectPattern = new Regex(@"
http://www\.microsoft\.com/.*");
WebPermission webPermissions = new WebPermission(NetworkAccess.Connect, connectPattern);
webPermissions.Assert();
// Method logic

To successfully use Assert, the assembly must have the SecurityPermissionFlag.Assertion privilege as well as the privilege being asserted. In the .NET Framework Configuration tool, SecurityPermissionFlag.Assertion is represented by the Assert Any Permission That Has Been Granted item in the Security permission properties dialog box. The FullTrust, LocalIntranet, and Everything permission sets have this permission.

Using Assert allows an assembly to vouch for the security of lesser-privileged assemblies. This is an excellent way to grant additional functionality to assemblies that would normally lack CAS permissions. For example, you can use an Assert to allow an assembly in the Internet zone to save a file to the user’s disk. Simply create an assembly with the AllowPartiallyTrustedCallersAttribute. Then create a public method that writes the file, create a FileIOPermission object, and call the Assert method before writing the file. The assembly in the Internet zone can save a file to a user’s disk without requiring the administrators to grant file permissions to the Internet zone.

To decrease the opportunity for an attacker to abuse asserted permissions, use the CodeAccessPermission.RevertAssert static method. As the name suggests, calling this method erases the assertion and returns CAS permission checking to the normal state. Use a try/finally block to ensure that you call RevertAssert after every Assert, even if a failure occurs. The following method demonstrates this and is also an excellent example of how to fail to a more secure permission set:

FileIOPermission filePermissions = new FileIOPermission(FileIOPermissionAccess.Write, @"C:\Inetpub\");
filePermissions.Assert();

try
{
          StreamWriter newFile = new StreamWriter(@"C:\Inetpub\NewFile.txt");
          newFile.WriteLine("Lesser privileged applications can save a file.");
          newFile.Close();
}
finally
{
          CodeAccessPermission.RevertAssert();
}

Assert does have a few limitations. You can use Assert only once in a method. If you have to assert multiple permissions, you need to create a custom permission set (described later in this lesson). Second, Assert doesn’t override the operating system’s role-based security, regardless of the assembly’s CAS permissions. If a user lacks permission to write to the D drive and runs an assembly with full trust that asserts that file permission, the Assert will succeed, but the assembly still can’t write to the D drive. The assembly is still limited by the user’s access restrictions.

How to Call Trusted Code from Partially Trusted Code

To prevent partially trusted code from bypassing security checks, partially trusted code can’t call strong-named assemblies by default. You can control this on an assembly-by-assembly basis, however, by adding the AllowPartiallyTrustedCallersAttribute assembly-level custom attribute:

[assembly:AllowPartiallyTrustedCallers]

If your assembly doesn’t have a strong name, partially trusted code can access your public methods even when you don’t add that attribute.

How to Use Permission Sets

Permission sets are a collection of permissions that can be used imperatively in the same ways you use individual permissions. Use the System.Security.Permissions.PermissionSet class to create a permission set and then use the AddPermission method to specify the permissions that define the permission set. Then you can call any standard permission methods, including Assert, Demand, Deny, and PermitOnly.

For example, the following code creates a permission set consisting of read access to the C:\Windows folder, write access to the C:\Inetpub\ folder, and read access to the HKEY_LOCAL_MACHINE\Software registry key. Then it demands access to all those resources to cause the runtime to throw an exception if any of the specified permissions are not available.

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

You can caxll Assert only once in a method, so if you need to assert multiple permissions, you must use a permission set.

Microsoft Patterns & Practices

About justindeveloper

I am MCP (Microsoft Certified Professional). MCTS (Microsoft Certified Technology Specialist) and MCPD (Microsoft Certified Professional Developer), also I am SAP Business One Certified!! Desarrollando desde el IDE de Visual Studio NET 2003 hasta ahora con el Visual Studio NET 2010. Desde Microsoft SQL Server 2000 hasta ahora con el Microsoft SQL Server 2008 R2 y tambien con SharePoint, desde WSS 3.0 y MOSS 2007 y ahora familirizandome con el Sharepoint Foundation 2010 & Sharepoint Server 2010. The software development will follow being every time more wonderful!
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s