Share via


Chapter 6 - .NET Security Overview

 

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

Last Revised: January 2006

Applies to:

  • .NET Framework version 1.1

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: This chapter provides an overview of .NET Framework security, contrasting user or role-based security with code access security. It also examines the security namespaces provided by the .NET Framework base class library to show you the types that you have at your disposal to program .NET Framework security.

Contents

In This Chapter
Overview
How to Use This Chapter
Managed Code Benefits
User vs. Code Security
.NET Framework Role-Based Security
.NET Framework Security Namespaces
Summary
Additional Resources

In This Chapter

  • Security benefits of managed code
  • Role-based security versus code access security
  • Principals and identities
  • PrincipalPermission objects
  • .NET Framework role-based security fundamentals
  • .NET Framework security namespaces

Overview

The Microsoft .NET Framework gives numerous techniques and a vast range of types in the security namespaces to help you build secure code and create secure Web applications. This chapter defines the .NET Framework security landscape by briefly introducing the security benefits of managed code development. This chapter also introduces and contrasts the two complimentary forms of security that are available to .NET Framework applications: user security and code security. Finally, the chapter briefly examines the security namespaces that you use to program .NET Framework security.

This chapter emphasizes how .NET Framework security applies to ASP.NET Web applications and Web services.

How to Use This Chapter

This chapter describes the security benefits inherent in using the .NET Framework and explains the complementary features of .NET Framework user (or role-based) security and .NET Framework code-based (or code access) security. We recommend that you use this chapter as follows:

  • Understand the two-layered defense provided by the .NET Framework. Role-based security allows you to control user access to application resources and operations, while code access security can control which code can access resources and perform privileged operations.
  • Create applications that use the security concepts in this chapter. This chapter tells you when you should use user-based security and when you should use code-based security. After reading this chapter, you will be able to identify how any new applications you create can be more secure by using role-based or code-based security.

Managed Code Benefits

Developing .NET Framework applications provides you with some immediate security benefits, although there are still many issues for you to think about. These issues are discussed in the Building chapters in Part III of this guide.

.NET Framework assemblies are built with managed code. Compilers for languages, such as the Microsoft Visual C#® development tool and Microsoft Visual Basic® .NET development system, output Microsoft intermediate language (MSIL) instructions, which are contained in standard Microsoft Windows portable executable (PE) .dll or .exe files. When the assembly is loaded and a method is called, the method's MSIL code is compiled by a just-in-time (JIT) compiler into native machine instructions, which are subsequently executed. Methods that are never called are not JIT-compiled.

The use of an intermediate language coupled with the run-time environment provided by the common language runtime offers assembly developers immediate security advantages.

  • File format and metadata validation. The common language runtime verifies that the PE file format is valid and that addresses do not point outside of the PE file. This helps provide assembly isolation. The common language runtime also validates the integrity of the metadata that is contained in the assembly.
  • Code verification. The MSIL code is verified for type safety at JIT compile time. This is a major plus from a security perspective because the verification process can prevent bad pointer manipulation, validate type conversions, check array bounds, and so on. This virtually eliminates buffer overflow vulnerabilities in managed code, although you still need to carefully inspect any code that calls unmanaged application programming interfaces (APIs) for the possibility of buffer overflow.
  • Integrity checking. The integrity of strong named assemblies is verified using a digital signature to ensure that the assembly has not been altered in any way since it was built and signed. This means that attackers cannot alter your code in any way by directly manipulating the MSIL instructions.
  • Code access security. The virtual execution environment provided by the common language runtime allows additional security checks to be performed at runtime. Specifically, code access security can make various run-time security decisions based on the identity of the calling code.

User vs. Code Security

User security and code security are two complementary forms of security that are available to .NET Framework applications. User security answers the questions, "Who is the user and what can the user do?" while code security answers the questions "Where is the code from, who wrote the code, and what can the code do?" Code security involves authorizing the application's (not the user's) access to system-level resources, including the file system, registry, network, directory services, and databases. In this case, it does not matter who the end user is, or which user account runs the code, but it does matter what the code is and is not allowed to do.

The .NET Framework user security implementation is called role-based security. The code security implementation is called code access security.

Role-Based Security

.NET Framework role-based security allows a Web application to make security decisions based on the identity or role membership of the user that interacts with the application. If your application uses Windows authentication, then a role is a Windows group. If your application uses other forms of authentication, then a role is application-defined and user and role details are usually maintained in SQL Server or user stores based on Active Directory.

The identity of the authenticated user and its associated role membership is made available to Web applications through Principal objects, which are attached to the current Web request.

Note   When using the Role Manager feature in ASP.NET 2.0, if the roles are application-defined, the role information is not made available through Principal objects. Instead, the Role Manager obtains role information directly from the role store. For more information see How To: Use Role Manager in ASP.NET 2.0.

Figure 6.1 shows a logical view of how user security is typically used in a Web application to restrict user access to Web pages, business logic, operations, and data access.

Ff648652.f06thcm01(en-us,PandP.10).gif

Figure 6.1

A logical view of (user) role-based security

Code Access Security

Code access security authorizes code when it attempts to access secured resources, such as the file system, registry, network, and so on, or when it attempts to perform other privileged operations, such as calling unmanaged code or using reflection.

Code access security is an important additional defense mechanism that you can use to provide constraints on a piece of code. An administrator can configure code access security policy to restrict the resource types that code can access and the other privileged operations it can perform. From a Web application standpoint, this means that in the event of a compromised process where an attacker takes control of a Web application process or injects code to run inside the process, the additional constraints that code access security provides can limit the damage that can be done.

Figure 6.2 shows a logical view of how code access security is used in a Web application to constrain the application's access to system resources, resources owned by other applications, and privileged operations, such as calling unmanaged code.

Ff648652.f06thcm02(en-us,PandP.10).gif

Figure 6.2

Logical view of code-based security

The authentication (identification) of code is based on evidence about the code, for example, its strong name, publisher, or installation directory. Authorization is based on the code access permissions granted to code by security policy. For more information about .NET Framework code access security, see Chapter 8, "Code Access Security in Practice."

.NET Framework Role-Based Security

.NET Framework role-based security is a key technology that is used to authorize a user's actions in an application. Roles are often used to enforce business rules. For example, a financial application might allow only managers to perform monetary transfers that exceed a particular threshold.

Role-based security consists of the following elements:

  • Principals and identities
  • PrincipalPermission objects
  • Role-based security checks
  • URLauthorization

Principals and Identities

Role-based security is implemented with Principal and Identity objects. The identity and role membership of the authenticated caller is exposed through a Principal object, which is attached to the current Web request. You can retrieve the object by using the HttpContext.Current.User property. If the caller is not required to authenticate with the application, for example, because the user is browsing a publicly accessible part of the site, the Principal object represents the anonymous Internet user.

Note   When using the Role Manager feature in ASP.NET 2.0, if the roles are application-defined, the role information is not made available through Principal objects. Instead, the Role Manager obtains role information directly from the role store. For more information see How To: Use Role Manager in ASP.NET 2.0.

There are many types of Principal objects and the precise type depends on the authentication mechanism used by the application. However, all Principal objects implement the System.Security.Principal.IPrincipal interface and they all maintain a list of roles of which the user is a member.

Principal objects also contain Identity objects, which include the user's name, together with flags that indicate the authentication type and whether or not the user has been authenticated. This allows you to distinguish between authenticated and anonymous users. There are different types of Identity objects, depending on the authentication type, although all implement the System.Security.Principal.IIdentity interface.

The following table shows the range of possible authentication types and the different types of Principal and Identity objects that ASP.NET Web applications use.

Table 6.1   Principal and Identity Objects Per Authentication Type

Authentication Type Principal and
Identity Type

Comments
Windows WindowsPrincipal +

WindowsIdentity

Verification of credentials is automatic and uses the Security Accounts Manager (SAM) or Active Directory. Windows groups are used for roles.
Forms GenericPrincipal +

FormsIdentity

You must add code to verify credentials and retrieve role membership from a user store.
Note   If you are using the Role Manager feature in ASP.NET 2.0, you do not need to write the code for retrieving user roles. For more information see How To: Use Role Manager in ASP.NET 2.0.
Passport GenericPrincipal +

PassportIdentity

Relies on the Microsoft Passport SDK. PassportIdentity provides access to the passport authentication ticket.

PrincipalPermission Objects

The PrincipalPermission object represents the identity and role that the current principal must have to execute code. PrincipalPermission objects can be used declaratively or imperatively in code.

Declarative Security

You can control precisely which users should be allowed to access a class or a method by adding a PrincipalPermissionAttribute to the class or method definition. A class-level attribute automatically applies to all class members unless it is overridden by a member-level attribute. The PrincipalPermissionAttribute type is defined within the System.Security.Permissions namespace.

Note   You can also use the PrincipalPermissionAttribute to restrict access to structures and to other member types, such as properties and delegates.

The following example shows how to restrict access to a particular class to members of a Managers group. Note that this example assumes Windows authentication, where the format of the role name is in the format MachineName\RoleName or DomainName\RoleName. For other authentication types, the format of the role name is application specific and depends on the role-name strings held in the user store.

[PrincipalPermissionAttribute(SecurityAction.Demand, Role=@"DOMAINNAME\Managers")]
public sealed class OnlyManagersCanCallMe
{
}

Note   The trailing Attribute can be omitted from the attribute type names. This makes the attribute type name appear to be the same as the associated permission type name, which in this case is PrincipalPermission. They are distinct (but logically related) types.

The next example shows how to restrict access to a particular method on a class. In this example, access is restricted to members of the local administrators group, which is identified by the special "BUILTIN\Administrators" identifier.

[PrincipalPermissionAttribute(SecurityAction.Demand,
                              Role=@"BUILTIN\Administrators")]
public void SomeMethod()
{
}

Other built-in Windows group names can be used by prefixing the group name with "BUILTIN\" (for example, "BUILTIN\Users" and "BUILTIN\Power Users").

Imperative Security

If method-level security is not granular enough for your security requirements, you can perform imperative security checks in code by using System.Security.Permissions.PrincipalPermission objects.

The following example shows imperative security syntax using a PrincipalPermission object.

PrincipalPermission permCheck = new PrincipalPermission(
                                    null, @"DomainName\WindowsGroup");
permCheck.Demand();

To avoid a local variable, the code above can also be written as:

(new PrincipalPermission(null, @"DomainName\WindowsGroup")).Demand();

The code creates a PrincipalPermission object with a blank user name and a specified role name, and then calls the Demand method. This causes the common language runtime to interrogate the current Principal object that is attached to the current thread and check whether the associated identity is a member of the specified role. Because Windows authentication is used in this example, the role check uses a Windows group. If the current identity is not a member of the specified role, a SecurityException is thrown.

The following example shows how to restrict access to an individual user.

(new PrincipalPermission(@"DOMAINNAME\James", null)).Demand();

Declarative vs. Imperative Security

You can use role-based security (and code access security) either declaratively using attributes or imperatively in code. Generally, declarative security offers the most benefits, although sometimes you must use imperative security (for example, when you need to use variables that are only available at runtime) to help make a security decision.

Advantages of Declarative Security

The main advantages of declarative security are the following:

  • It allows the administrator or assembly consumer to see precisely which security permissions that particular classes and methods must run. Tools such as permview.exe provide this information. Knowing this information at deployment time can help resolve security issues and it helps the administrator configure code access security policy.
  • It offers increased performance. Declarative demands are evaluated only once at load time. Imperative demands inside methods are evaluated each time the method that contains the demand is called.
  • Security attributes ensure that the permission demand is executed before any other code in the method has a chance to run. This eliminates potential bugs where security checks are performed too late.
  • Declarative checks at the class level apply to all class members. Imperative checks apply at the call site.

Advantages of Imperative Security

The main advantages of imperative security and the main reasons that you sometimes must use it are:

  • It allows you to dynamically shape the demand by using values only available at runtime.
  • It allows you to perform more granular authorization by implementing conditional logic in code.

Role-Based Security Checks

For fine-grained authorization decisions, you can also perform explicit role checks by using the IPrincipal.IsInRole method. The following example assumes Windows authentication, although the code would be very similar for Forms authentication, except that you would cast the User object to an object of the GenericPrincipal type.

// Extract the authenticated user from the current HTTP context.
// The User variable is equivalent to HttpContext.Current.User if you are using
// an .aspx or .asmx page
WindowsPrincipal authenticatedUser = User as WindowsPrincipal;
if (null != authenticatedUser)
{
  // Note: If you need to authorize specific users based on their identity
  // and not their role membership, you can retrieve the authenticated user's
  // username with the following line of code (normally though, you should
  // perform role-based authorization).
  // string username = authenticatedUser.Identity.Name;

  // Perform a role check
  if (authenticatedUser.IsInRole(@"DomainName\Manager") )
  {
    // User is authorized to perform manager functionality
  }
}
else
{
  // User is not authorized to perform manager functionality
  // Throw a security exception
}

Note When using the Role Manager feature in ASP.NET 2.0, you can also use the Roles API such as Roles.IsUserInRole for role checks. For more information, see "How To: Use Role Manager in ASP.NET 2.0."

URL Authorization

Administrators can configure role-based security by using the <authorization> element in Machine.config or Web.config. This element configures the ASP.NET UrlAuthorizationModule, which uses the principal object attached to the current Web request in order to make authorization decisions.

The authorization element contains child <allow> and <deny> elements, which are used to determine which users or groups are allowed or denied access to specific directories or pages. Unless the <authorization> element is contained within a <location> element, the <authorization> element in Web.config controls access to the directory in which the Web.config file resides. This is normally the Web application's virtual root directory.

The following example from Web.config uses Windows authentication and allows Bob and Mary access but denies everyone else:

<authorization>
  <allow users="DomainName\Bob, DomainName\Mary" />
  <deny users="*" />
</authorization>

The following syntax and semantics apply to the configuration of the <authorization> element:

  • "*" refers to all identities.

  • "?" refers to unauthenticated identities (that is, the anonymous identity).

  • You do not need to impersonate for URL authorization to work.

  • Users and roles for URL authorization are determined by your authentication settings:

    Note   When using the Role Manager feature in ASP.NET 2.0, you only need to enable Role Manager and configure the role provider to point to the role store. Because the IPrincipal object internally uses the Role Manager, you do not need to explicitly set the role information in the Principal object. For more information see How To: Use Role Manager in ASP.NET 2.0.

    • When you have <authentication mode="Windows" />, you are authorizing access to Windows user and group accounts.

      User names take the form "DomainName\WindowsUserName".

      Role names take the form "DomainName\WindowsGroupName".

      Note   The local administrators group is referred to as "BUILTIN\Administrators". The local users group is referred to as "BUILTIN\Users".

    • When you have <authentication mode="Forms" />, you are authorizing against the user and roles for the IPrincipal object that was stored in the current HTTP context. For example, if you used Forms to authenticate users against a database, you will be authorizing against the roles retrieved from the database.

    • When you have <authentication mode="Passport" />, you authorize against the Passport User ID (PUID) or roles retrieved from a store. For example, you can map a PUID to a particular account and set of roles stored in a Microsoft SQL Server database or Active Directory.

    • When you have <authentication mode="None" />, you may not be performing authorization. "None" specifies that you do not want to perform any authentication or that you do not want to use any of the ASP.NET authentication modules, but you do want to use your own custom mechanism.

      However, if you use custom authentication, you should create an IPrincipal object with roles and store it in the HttpContext.Current.User property When you subsequently perform URL authorization, it is performed against the user and roles (no matter how they were retrieved) maintained in the IPrincipal object.

Configuring Access to a Specific File

To configure access to a specific file, place the <authorization> element inside a <location> element as shown below.

<location path="somepage.aspx" />
  <authorization>
    <allow users="DomainName\Bob, DomainName\Mary" />
    <deny users="*" />
  </authorization>
</location>

You can also point the path attribute at a specific folder to apply access control to all the files in that particular folder. For more information about the <location> element, see Chapter 19, "Securing Your ASP.NET Application and Web Services."

.NET Framework Security Namespaces

To program .NET Framework security, you use the types in the .NET Framework security namespaces. This section introduces these namespaces and the types that you are likely to use when you develop secure Web applications. For a full list of types, see the .NET Framework documentation. The security namespaces are listed below and are shown in Figure 6.3.

  • System.Security
  • System.Web.Security
  • System.Security.Cryptography
  • System.Security.Principal
  • System.Security.Policy
  • System.Security.Permissions

**Note   **The .NET Framework 2.0 has introduced new security-related namespaces such as System.Security.AccessControl, System.Security.Cryptography.Pkcs, System.Security.Cryptography.X509Certificates, System.Security.Cryptography.Xml, and System.Net.Security. For more information, see .NET Framework Class Library.

Ff648652.f06thcm03(en-us,PandP.10).gif

Figure 6.3

.NET Framework security namespaces in .NET 1.1

System.Security

This namespace contains the CodeAccessPermission base class from which all other code access permission types derive. You are unlikely to use the base class directly. You are more likely to use specific permission types that represent the rights of code to access specific resource types or perform other privileged operations. For example, FileIOPermission represents the rights to perform file I/O, EventLogPermission represents the rights for code to access the event log, and so on. For a full list of code access permission types, see Table 6.2 later in this chapter.

The System.Security namespace also contains classes that encapsulate permission sets. These include the PermissionSet and NamedPermissionSet classes. The types you are most likely to use when building secure Web applications are:

  • SecurityException. The exception type used to represent security errors.

    **Note   **In .NET 2.0, the SecurityException class has been improved to facilitate debugging security issues and provide detailed information about security exceptions.

  • AllowPartiallyTrustedCallersAttribute. An assembly-level attribute used with strong named assemblies that must support partial trust callers. Without this attribute, a strong named assembly can only be called by full trust callers (callers with unrestricted permissions.)

  • SupressUnmanagedSecurityAttribute. Used to optimize performance and eliminate the demand for the unmanaged code permission issued by the Platform Invocation Services (P/Invoke) and Component Object Model (COM) interoperability layers. This attribute must be used with caution because it exposes a potential security risk. If an attacker gains control of unmanaged code, he is no longer restricted by code access security. For more information about using this attribute safely, see "Unmanaged Code" in Chapter 8, "Code Access Security in Practice."

    Note   In .NET 2.0, the System.Security namespace has new classes such as SecurityContext and SecureString. For more information, see .NET Framework Class Library - System.Security Namespace.

System.Web.Security

This namespace contains the classes used to manage Web application authentication and authorization. This includes Windows, Forms, and Passport authentication and URL and File authorization, which are controlled by the UrlAuthorizationModule and FileAuthorizationModule classes, respectively. The types you are most likely to use when you build secure Web applications are:

  • FormsAuthentication. Provides static methods to help with Forms authentication and authentication ticket manipulation.
  • FormsIdentity. Used to encapsulate the user identity that is authenticated by Forms authentication.
  • PassportIdentity. Used to encapsulate the user identity that is authenticated by Passport authentication.

System.Security.Cryptography

This namespace contains types that are used to perform encryption and decryption, hashing, and random number generation. This is a large namespace that contains many types. Many encryption algorithms are implemented in managed code, while others are exposed by types in this namespace that wrap the underlying cryptographic functionality provided by the Microsoft Win32®-based CryptoAPI.

System.Security.Principal

This namespace contains types that are used to support role-based security. They are used to restrict which users can access classes and class members. The namespace includes the IPrincipal and IIdentity interfaces. The types you are most likely to use when building secure Web applications are:

  • GenericPrincipal and GenericIdentity. Allow you to define your own roles and user identities. These are typically used with custom authentication mechanisms.

  • WindowsPrincipal and WindowsIdentity. Represents a user who is authenticated with Windows authentication together with the user's associated Windows group (role) list.

    Note   In .NET 2.0, the WindowsIdentity class now supports a new constructor that accepts a user name represented by the user principal name (UPN). This constructor uses the Kerberos S4U (Service-for-User) extension to obtain a Windows token for the user.

System.Security.Policy

This namespace contains types that are used to implement the code access security policy system. It includes types to represent code groups, membership conditions, policy levels, and evidence.

System.Security.Permissions

This namespace contains the majority of permission types that are used to encapsulate the rights of code to access resources and perform privileged operations. The following table shows the permission types that are defined in this namespace (in alphabetical order).

Table 6.2   Permission Types Within the System.Security.Permissions Namespace

Permission Description
DirectoryServicesPermission Required to access Active Directory.
DNSPermission Required to access domain name system (DNS) servers on the network.
EndpointPermission Defines an endpoint that is authorized by a SocketPermission object.
EnvironmentPermission Controls read and write access to individual environment variables. It can also be used to restrict all access to environment variables.
EventLogPermission Required to access the event log.
FileDialogPermission Allows read-only access to files only if the file name is specified by the interactive user through a system-provided file dialog box. It is normally used when FileIOPermission is not granted.
FileIOPermission Controls read, write, and append access to files and directory trees. It can also be used to restrict all access to the file system.
IsolatedStorageFilePermission Controls the usage of an application's private virtual file system (provided by isolated storage). Isolated storage creates a unique and private storage area for the sole use by an application or component.
IsolatedStoragePermission Required to access isolated storage.
MessageQueuePermission Required to access Microsoft Message Queuing message queues.
OdbcPermission Required to use the ADO.NET ODBC data provider. (Full trust is also required.)
OleDbPermission Required to use the ADO.NET OLE DB data provider. (Full trust is also required.)
OraclePermission Required to use the ADO.NET Oracle data provider. (Full trust is also required.)
PerformanceCounterPermission Required to access system performance counters.
PrincipalPermission Used to restrict access to classes and methods based on the identity and role membership of the user.
PrintingPermission Required to access printers.
ReflectionPermission Controls access to metadata. Code with the appropriate ReflectionPermission can obtain information about the public, protected, and private members of a type.
RegistryPermission Controls read, write, and create access to registry keys (including subkeys). It can also be used to restrict all access to the registry.
SecurityPermission This is a meta-permission that controls the use of the security infrastructure itself.
ServiceControllerPermission Can be used to restrict access to the Windows Service Control Manager and the ability to start, stop, and pause services.
SocketPermission Can be used to restrict the ability to make or accept a connection on a transport address.
SqlClientPermission Can be used to restrict access to SQL Server data sources.
UIPermission Can be used to restrict access to the clipboard and to restrict the use of windows to "safe" windows in an attempt to avoid attacks that mimic system dialog boxes that prompt for sensitive information such as passwords.
WebPermission Can be used to control access to HTTP Internet resources.

The SecurityPermission class warrants special attention because it represents the rights of code to perform privileged operations, including asserting code access permissions, calling unmanaged code, using reflection, and controlling policy and evidence, among others. The precise right determined by the SecurityPermission class is determined by its Flags property, which must be set to one of the enumerated values defined by the SecurityPermissionFlags enumerated type (for example, SecurityPermissionFlags.UnmanagedCode).

Note   The .NET Framework 2.0 has introduced new permissions such as DataProtectionPermission, GacIdentityPermission, KeyContainerPermission, and StorePermission. For more information, see .NET Framework Class Library - System.Security.Permissions Namespace.

Summary

This chapter has introduced you to the .NET Framework security landscape by contrasting user security and code security and by examining the security namespaces. The .NET Framework refers to these two types of security as role-based security and code access security, respectively. Both forms of security are layered on top of Windows security.

Role-based security is concerned with authorizing user access to application-managed resources (such as Web pages) and operations (such as business and data access logic). Code access security is concerned with constraining privileged code and controlling precisely which code can access resources and perform other privileged operations. This is a powerful additional security mechanism for Web applications because it restricts what an attacker is able to do, even if the attacker manages to compromise the Web application process. It is also an extremely powerful feature for providing application isolation. This is particularly true for hosting companies or any organization that hosts multiple Web applications on the same Web server.

Additional Resources

For more information, see the following resources:

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.