How To: Use Code Access Security Policy to Constrain an Assembly

 

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

patterns & practices Developer Center

Improving Web Application Security: Threats and Countermeasures

J.D. Meier, Alex Mackman, Michael Dunner, Srinath Vasireddy, Ray Escamilla and Anandha Murukan

Microsoft Corporation

Published: June 2003

Applies to:

  • .NET Framework version 1.1
  • .NET Framework version 2.0

See the "patterns & practices Security Guidance for Applications Index" for links to additional security resources.

See the Landing Page for the starting point and a complete overview of Improving Web Application Security: Threats and Countermeasures.

Summary: An administrator can configure code access security policy to constrain the operations of .NET Framework code (assemblies.) In this How To, you configure code access security policy to constrain the ability of an assembly to perform file I/O and restrict file I/O to a specific directory.

You use the.NET Framework 1.1 / 2.0 Configuration tool to create a new permission set and a new code group. The permission set defines what the code can and cannot do, and the code group associates the permission set with particular code, for example a specific assembly or set of assemblies.

In addition to constraining file I/O, you can use code access security policy to impose other constraints on code. For example, you can restrict the ability of code to access other types of resources protected by code access security, including databases, directory services, event log, registry, Domain Name System (DNS) servers, unmanaged code, and environment variables.

Note   This list is not exhaustive but represents many of the common resource types accessed by Web applications.

Contents

Before You Begin Summary of Steps Step 1. Create an Assembly That Performs File I/O Step 2. Create a Web Application Step 3. Test File I/O with No Code Access Security Constraints Step 4. Configure Code Access Security Policy to Constrain File I/O Step 5. Test File I/O With Code Access Security Constraints

Before You Begin

Before you begin to use code access security policy to constrain an assembly, you should be aware of the following:

  • To constrain a Web application so that it is only able to access files within its own virtual directory hierarchy, you can configure the application to run with medium trust by placing the following in Web.config:

    <system.web>
      <trust level="Medium" />
    </system.web>
    

    This uses ASP.NET code access security policy to constrain the ability of the Web application to perform file I/O and it also imposes other constraints. For example, a medium trust application cannot directly access the event log, registry, or OLE DB data sources.

  • ASP.NET code access security policy is configured independently from enterprise-level, machine-level, and user-level code access security policy. The.NET Framework version 1.1 / 2.0 Configuration tool only supports enterprise-level, machine-level, and user-level policy.

    You must maintain ASP.NET policy by using a text or XML editor. For more information about running Web applications using medium trust, see Chapter 9, "Using Code Access Security with ASP.NET."

  • When you build an assembly, you can impose constraints programmatically using code access security. For more information about how to do this, see Chapter 8, "Code Access Security in Practice."

  • You should generally avoid building Web applications that accept file names and paths from the user because of the security risks posed by canonicalization issues. On occasion, you might need to accept a file name as input. This How To shows you how you can constrain an assembly to ensure that it cannot access arbitrary parts of the file system. For more information about performing file I/O, see "File I/O" sections in Chapter 7, "Building Secure Assemblies" and Chapter 8, "Code Access Security in Practice," of Improving Web Application Security.

  • For more information about code access security fundamentals, see Chapter 8, "Code Access Security in Practice," of Improving Web Application Security.

Summary of Steps

This How To includes the following steps:

  1. Create an assembly that performs file I/O.
  2. Create a Web application.
  3. Test file I/O with no code access security constraints.
  4. Configure code access security policy to constrain file I/O.
  5. Test file I/O with code access security constraints.

Step 1. Create an Assembly That Performs File I/O

In this step, you create an assembly that performs file I/O using a supplied filename.

To create a new assembly that performs file I/O

  1. Create a new Microsoft Visual C#® development tool class library project called FileIO and rename class1.cs to FileIO.cs.

  2. Add a strong name to the assembly.

    By adding a strong name, you make the assembly tamper proof by digitally signing it. The public key component of the strong name also provides cryptographically strong evidence for code access security policy. An administrator can apply policy by using the strong name to uniquely identify the assembly.

  3. Use a fixed assembly version. Open Assemblyinfo.cs and set the AssemblyVersion attribute as shown below:

    [assembly: AssemblyVersion("1.0.0.1")]
    
  4. Add the following using statements to the top of FileIO.cs:

    using System.IO;
    using System.Text;
    
  5. Rename Class1 to FileWrapper and seal the class to prevent inheritance.

    public sealed class FileWrapper
    
  6. Rename the default constructor to match the class name and change it to private, which prevents instances of the FileWrapper class from being created. This class provides static methods only.

  7. Add the following public method so that it reads from a specified file.

    public static string ReadFile(string filename)
    {
      byte[] fileBytes = null;
      long fileSize = -1;
      Stream fileStream = null;
    
      try
      {
        if(null == filename)
        {
          throw new ArgumentException("Missing filename");
        }
        // Canonicalize and validate the supplied filename
        // GetFullPath:
        // - Checks for invalid characters (defined by Path.InvalidPathChars)
        // - Checks for Win32 non file-type device names including
        //   physical drives, parallel and serial ports, pipes, mail slots,
        //   and so on
        // - Normalizes the file path
    
        filename = Path.GetFullPath(filename);
        fileStream = File.OpenRead(filename);
        if(!fileStream.CanRead)
        {
          throw new Exception("Unable to read from file.");
        }
        fileSize = fileStream.Length;
        fileBytes = new byte[fileSize];
        fileStream.Read(fileBytes, 0, Convert.ToInt32(fileSize));
        return Encoding.ASCII.GetString(fileBytes);
      }
      catch (Exception ex)
      {
        throw ex;
      }
      finally
      {
        if (null != fileStream)
          fileStream.Close();
      }
    }
    

Step 2. Create a Web Application

In this step, you create a Web application assembly that calls the file I/O assembly.

To create a Web application

  1. Add a new C# ASP.NET Web application project called FileIOWeb to the current solution.

  2. Add a project reference in the new project that references the FileIO project.

  3. Add a text box to WebForm1.aspx to allow the user to supply a path and filename. Set its Text property to c:\temp\somefile.txt and set its ID to txtFileName.

  4. Add a button to WebForm1.aspx and set its Text property to Read File and its ID to btnReadFile.

  5. Double-click the Read File button and add the following code to the event handler:

    string s = FileIO.FileWrapper.ReadFile( txtFileName.Text );
    Response.Write(s);
    
  6. Build the solution.

Step 3. Test File I/O with No Code Access Security Constraints

By default, Web applications and any assemblies they call on the local computer are granted full trust by code access security policy. The default <trust> configuration in Machine.config assigns the full trust level to all Web applications, as follows:

<trust level="Full" originUrl="" />

With full trust, Web applications are not constrained in any way by code access security policy. The success or failure of resource access is determined purely by operating system security.

To test file I/O with no code access security constraints

  1. Use Notepad to create a text file called Somefile.txt that contains a simple text string, and then place the file in the C:\temp directory. Also place a copy in the root C:\ directory.

  2. Run the Web application and click Read File.

    The contents of the text file are displayed.

  3. Enter c:\somefile.txt in the text box and click Read File.

    The contents of the text file are displayed.

Step 4. Configure Code Access Security Policy to Constrain File I/O

In this step, you configure code access security policy for the FileIO assembly and grant it a restricted FileIOPermission so that it is only able to access files from beneath C:\Temp. You start by creating a new permission set that includes a restricted FileIOPermission. You then create a new code group to associate the new permission set with the FileIO assembly by using strong name evidence.

To create a new permission set

  1. Start the .NET Framework version 1.1 / 2.0 Configuration tool from the Administrative Tools program folder.

  2. Expand the Runtime Security Policy node.

    Three levels of code access security policy are displayed: Enterprise, Machine, and User. The fourth level at which you can configure code access security policy is the application domain level. ASP.NET implements application domain level policy, but this is not maintained using the.NET Framework version 1.1 / 2.0 Configuration tool. To edit ASP.NET policy, you must use a text editor.

    For more information about ASP.NET policy and how to use it, see Chapter 9, "Using Code Access Security with ASP.NET."

  3. Expand the Machine node.

    The Code Groups and Permission Sets folders are displayed. Each policy file contains a hierarchical collection of code groups. Code groups are used to assign permissions to assemblies. A code group consists of two elements:

    • A membership condition — This is based on evidence, for example an assembly's strong name.
    • A permission set — The permissions that the permission set contains are granted to assemblies whose evidence matches the membership condition.

    A permission set is a grouping that contains a collection of individual code access security permissions. Individual permissions represent the rights for code to access specific resource types or perform specific types of privileged operations.

  4. Right-click Permission Sets, and then click New.

  5. Enter RestictedFileIO in the Name field, and then click Next.

  6. Select FileIO from the Available Permissions list, and then click Add.

  7. Enter c:\temp in the File Path column and select Read and PathDisc (path discovery.)

    Path discovery permissions are required by the Path.GetFullPath function that is used by the FileIO assembly to canonicalize and validate the supplied filename.

    Read permissions are required by the File.OpenRead method, which is used by the FileIO assembly to open the text file.

  8. Click OK.

  9. Select Security from the Available Permissions list and click Add.

    The FileIO assembly also needs the permission to execute in addition to the FileIOPermission. The permission to execute is represented by SecurityPermission with its Flags property set to SecurityPermissionFlag.Execution.

  10. Click Enable assembly execution, and then click OK.

  11. Click Finish to complete the creation of the permission set.

    You have now created a new permission set called RestrictedFileIO that contains a restricted FileIOPermission, which allows read and path discovery to the C:\Temp directory, and a restricted SecurityPermission, which allows assembly execution.

To create a new code group

  1. Expand Code Groups, and then expand All_Code.

  2. Right-click All_Code, and then click New.

  3. Enter FileIOAssembly as the code group name, and then click Next.

  4. Select StrongName from the Choose the condition type for this code group drop-down list.

    You use this code group to apply specific permissions as defined by the RestrictedFileIO permission set to the FileIO assembly. A strong name provides cryptographically strong evidence to uniquely identify an assembly.

  5. To specify the FileIO assembly's public key, (which it has because it contains a strong name), click Import, and then browse to the project output folder that contains FileIO.dll. Click Open to extract the public key from the assembly.

  6. Click Next, and then select RestrictedFileIO from the Use existing permission set drop-down list.

  7. Click Next and then Finish to complete the creation of the code group.

    You have now created a new code group that applies the permissions defined by the RestrictedFileIO permission set to the FileIO assembly.

  8. In the right window, select the FileIOAssembly code group, and then click Edit Code Group Properties.

  9. Select This policy level will only have the permissions from the permission set associated with this code group and Policy levels below this level will not be evaluated.

    By selecting these attributes for the code group, you ensure that no other code group, either at the current machine level or from the ASP.NET application domain level, affects the permission set that is granted to the FileIO assembly. This ensures that the assembly is only granted the permissions defined by the RestrictedFileIO permission set that you created earlier.

    Note   If you do not select these options, default machine policy grants the assembly full trust because the assembly is installed on the local computer and falls within the My_Computer_Zone setting.

  10. Click OK to close the Properties dialog box.

Step 5. Test File I/O With Code Access Security Constraints

In this procedure, you install the FileIO assembly in the global assembly cache (GAC). You then run the Web application and try to access files inside and outside of C:\Temp. The code access security policy that you configured in Step 4 constrains the code so that it is only allowed to access files from beneath C:\Temp.

The assembly should be installed in the GAC because of the ASP.NET loads strong named assemblies as domain neutral assemblies. All strong named assemblies that ASP.NET Web applications call should be installed in the GAC. For more information about this issue, see "Strong Names" in Chapter 7, "Building Secure Assemblies."

Note   Normally, default machine policy and ASP.NET policy grant full trust to assemblies that are installed in the GAC. The This policy level will only have the permissions from the permission set associated with this code group and Policy levels below this level will not be evaluated attributes that you assigned to the code group created in Step 4 ensure that the assembly is not granted full trust, and is only granted the permissions defined by the RestrictedFileIO permission set that you created earlier.

To test file I/O with code access security constraints

  1. Install the FileIO assembly into the GAC using the Gacutil.exe utility.

    You can call Gacutil.exe as a post-build step to ensure that it is placed in the GAC when it has been successfully built inside Microsoft Visual Studio® .NET.

    • Display the FileIO project's Properties dialog box in Visual Studio .NET.
    • In Common Properties, select Build Events.
    • Type "C:\Program Files\Microsoft Visual Studio {version number}\SDK\{version number}\Bin\gacutil" -i $(TargetPath) in the Post-build Event Command Line field.
    • Click OK to close the project Properties dialog box.
  2. Rebuild the solution.

  3. Run Iisreset.exe from a command line to force the ASP.NET process to be recycled.

    This forces the permission grant for the FileIO assembly to be recomputed. If the ASP.NET application domain is still active from the last time you ran the Web application, the assembly could still be cached by ASP.NET.

  4. Run the Web application, and then click Read File.

    The contents of the text file should be successfully displayed. The policy that you created allows the FileIO assembly to read files from C:\Temp and below.

  5. Enter C:\somefile.txt in the text box, and then click Read File.

    A SecurityException should be generated because the code access security policy that you configured does not allow file I/O outside of the C:\Temp directory.

    The exception details indicate that a request for the FileIOPermission has failed, as shown below:

    System.Security.SecurityException: Request for the permission of type System.Security.Permissions.FileIOPermission, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 failed.
    

patterns & practices Developer Center

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

© Microsoft Corporation. All rights reserved.