December 20, 2014
Hot Topics:

Code Access Security with Microsoft .NET Framework, Part 2

  • March 10, 2005
  • By Mark Strawmyer
  • Send Email »
  • More Articles »

This .NET Nuts & Bolts installment is the second part of an exploration of code access security and what it means in the Microsoft .NET Framework. It builds on the examination of evidence-based security in Part 1 and looks at imperative and declarative security checks, as well as how to override security checks.

Security Checks

The Microsoft .NET Framework provides imperative and declarative ways to check for proper permissions prior to attempting an action. Imperative security involves the use of permission objects at run time to do an explicit check in code. The permission objects, located in the System.Security.Permissions namespace, have a Demand() method that determines whether or not the desired action can be taken according to the security policy assigned to the assembly. There are permission objects for security permissions such as file IO, access to environment variables, registry access, ability to use reflection, and more. Declarative security uses attributes on a class or method to dictate that the code has to have the desired permission or it will throw a security exception when the application starts. This prevents the code from running unless all of the requested permissions are allowed.

Imperative Security Check Sample Code

The sample code from Part 1 could be replaced with the following sample code, which performs an imperative security check. (Note: Follow the samples in Part 1 to establish the proper code groups and permission sets for the runtime security policy.) It relies on the FileIOPermission in the System.Security.Permissions namespace to determine whether the assembly has access to the desired file permissions according to the evidence. The FileIOPermission object allows you to check for read, write, append, or path discovery. The read and write permissions are self-explanatory. The append permission is the ability to only write to the end of the file but not read it. The path discovery grants permission to read the actual path where the file is stored. All four permissions are exposed through a FileIOPermissionAccess enumeration, which also includes all access or no access. The desired combination of rights can be checked by using a bitwise of the necessary enumeration members. This example just checks for all access:

FileIOPermission filePermission = new   FileIOPermission(FileIOPermissionAccess.AllAccess,                     @"C:\helloworld.txt");try{   filePermission.Demand();   MessageBox.Show ("Permission demand successful");}catch( SecurityException securityEx ){   MessageBox.Show( securityEx.Message, "Security Exception" );}

When you execute the code, you'll notice that you receive a dialog indicating whether or not the required permissions exist.

Declarative Security Sample Code

Declarative security is controlled by attributes and is checked up front when the code begins to execute. The desired permissions must be granted or the code will stop execution before it starts. Add the following attribute at the class level of the sample form you created. In this case, modify the desired permissions by just requesting read permissions:

[FileIOPermission(System.Security.Permissions.SecurityAction.Demand,   Read= @"C:\helloworld.txt")]public class Form1 : System.Windows.Forms.Form{   ...}

Add or remove the permissions from the permission set as desired and play around with the sample. If you remove permission, the program throws an exception immediately when it starts to run.

Bypass Security Checks with Assert

You sometimes may want to allow your code to do something that it has permission to do but the person calling it (caller) does not. The code access permission classes, such as the previously demonstrated FileIOPermission, have an Assert() method, which you use to tell the security system not to check whether the person calling the code has the permission. This is useful in situations where it may not be obvious that the code is going to use a resource. For example, suppose your code uses the Registry for some reason and there is a good chance the caller doesn't have the required permissions nor do you want to require that permissions be granted to the user in order to execute the code. As long as your code has the permission and the security permission to make assertions such as by being fully trusted, you can assert permissions for the required item and skip the check whether the user has access.

In the previous examples, you could assert permissions to write an application log file to a location on the user's machine without allowing the user to do so outside of the application. You can use the assert method in either imperative or declarative fashion.

Assert Sample Code

Modify the imperative security check from the previous section to use the Assert() method rather than the Demand() method. As long as your code has permission to execute, such as through being fully trusted, the code will execute and bypass the checks for the user:

FileIOPermission filePermission = new   FileIOPermission(FileIOPermissionAccess.AllAccess,                    @"C:\helloworld.txt");try{   filePermission.Assert();   MessageBox.Show ("Permission assert successful");}catch( SecurityException securityEx ){   MessageBox.Show( securityEx.Message, "Security Exception" );}

Use the Assert() method with extreme caution. You can easily create situations where your code could be abused. For example, assume the code above was part of a utility method to read or write a file where you allow the caller to specify the name of the file. The caller could easily use your code to access other protected files. It is more appropriate to use the Assert() method in cases where you want to insure your application can do certain things, such as write to an application log file, and doesn't expose any of the actions to the caller.

Additional Topics

Having examined imperative and declarative permission checks and learned how to override security checks for the user, you should explore additional topics on your own. Some items of interest may include:

  • Deploy security policies through Active Directory policies. This will simplify the deployment within your organization and ensure that your machines have the desired permission sets.
  • Explore the other permission objects that exist in the System.Security.Permissions namespace for use in imperative security checks. There are a number of them for a variety of things.
  • Try using declarative checks to make sure that your code has access to execute unmanaged code.
  • Look into the use of Assert to overwrite security checks. You can employ the Assert method to grant your code access to protected resources and then remove it when it is completed. This is useful when you follow the best practice of running in an environment with least privileges, also commonly referred to as sandboxing your code. To use this technique, you should sign your code, load it in the global assembly cache (GAC) so that it has full trust permissions, and use Assert() and RevertAssert() to grant and remove permissions for the caller.

Future Columns

The next column has yet to be determined. If you have something in particular that you would like to see explained, please e-mail me at mstrawmyer@crowechizek.com.

About the Author

Mark Strawmyer, MCSD, MCSE, MCDBA is a Senior Architect of .NET applications for large and mid-size organizations. Mark is a technology leader with Crowe Chizek in Indianapolis, Indiana. He specializes in architecture, design, and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C# for the second year in a row. You can reach Mark at mstrawmyer@crowechizek.com.






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel