patterns & practices Developer Center J.D. Meier, Alex Mackman, Blaine Wastell, Prashant Bansode, Andy Wigley, Kishore Gopalan
Microsoft Corporation
August 2005
Applies To
Summary
This
module presents a set of consolidated ASP.NET version 2.0 security
guidelines. You can use these guidelines to learn security best
practices for design, implementation, and deployment. The guidelines
can also be used to check an existing application during security
review. Each guideline organizes key information and explains what to
do, why you should do it, and how you can implement it. Detailed
step-by-step instructions for more complex procedures required to
implement a guideline are provided by companion How To modules to which
the guidelines refer. This guideline module has a corresponding
checklist that summarizes the security guidelines. For the checklist,
see "Security Checklist: ASP.NET 2.0." This module also includes an index of guidelines for ASP.NET version 2.0 applications.
Contents
How to Use This Module
What's New in 2.0
Index of Guidelines
Input/Data Validation
Authentication
Forms Authentication
Windows Authentication
Authorization
Code Access Security
Data Access
Exception Management
Impersonation/Delegation
Parameter Manipulation
Sensitive Data
Session Management
Auditing and Logging
Deployment Considerations
Communication Security
Companion Guidance
Additional Resources
How to Use This Module
To get the most from this module:
- Use the index to browse the guidelines. Use the index to scan across the guidelines and to quickly jump to a specific guideline.
- Learn the guidelines. Browse
the guidelines to learn security best practices for design,
implementation, and deployment. Each guideline explains what to do,
why, and how.
- Use the companion How To modules. Refer
to the associated How To modules for more detailed step-by-step
implementation details. They describe how to implement the more complex
solution elements required to apply a guideline.
- Use the companion checklist. Use the associated checklist as a quick reference job aid to help you learn and implement the guidelines.
What's New in 2.0
The
.NET Framework version 2.0 and ASP.NET version 2.0 introduce many new
security features. The most notable enhancements for ASP.NET Web
applications are:
- Forms authentication and membership.
You can now use forms authentication with the new membership feature
and membership API. The membership feature supports a provider model,
with the SqlMembershipProvider for SQL Server databases and ActiveDirectoryMembershipProvider
for Microsoft Active Directory® and Active Directory Application Mode
(ADAM) stores provided as built-in providers. You can also create
custom providers for your custom user stores. You no longer have to
create your own custom databases and write your own custom
authentication code.
- Role manager. The new role
management feature provides protected role storage and an API for
managing and checking role membership. The role manager supports a
provider model. The supplied providers are:
- The SqlRoleProvider for SQL Server role stores.
- The WindowsTokenRoleProvider used with Windows authentication, which uses Windows groups as roles.
- The AuthorizationStoreRoleProvider, which uses Windows Server 2003 Authorization Manager for managing roles in Active Directory or ADAM.
- DPAPI managed wrapper. The
.NET Framework version 2.0 provides a set of managed classes to access
the Win32 Data Protection API (DPAPI). Code requires the DataProtectionPermission to be able to use DPAPI.
- Configuration file changes.
Machine-wide configuration settings for all Web applications on a
server are now maintained in a machine-level Web.config file instead of
Machine.config. The machine-level Web.config file is located in the
\Windows\Microsoft.NET\Framework\{version}\CONFIG directory.
- Configuration file encryption. ASP.NET
version 2.0 introduces a Protected Configuration feature to enable you
to encrypt sections of your Machine.config and Web.config files by
using either DPAPI or RSA encryption. This is particularly useful for
encrypting connection strings and account credentials.
- Health monitoring.
ASP.NET version 2.0 introduces a health monitoring system. It supports
many standard events that you can use to monitor the health of your
application. Examples of security related events that are automatically
generated include logon failures and successes when using the ASP.NET
membership system, attempts to tamper with or reuse forms
authentication tickets, and infrastructure events such as disk access
failures. You can also create custom events to instrument your
application for other security and non-security related notable events.
- Code access security.
The SQL Server managed data provider no longer demands Full trust. This
means that Medium trust Web applications can now access SQL Server
databases by using this provider. Also, in version 2.0, SmtpPermission is available at Full, High, and Medium trust levels. This allows partial trust Web applications to send e-mail.
- MachineKey enhancements. The <machineKey> now supports a decryption
attribute that specifies the symmetric encryption algorithm used to
encrypt and decrypt forms authentication tickets. ASP.NET version 2.0
provides support for AES symmetric encryption, which is used by
default, in addition to DES and 3DES.
- Impersonation token can be retained in new thread. In
.NET Framework 2.0, by default the impersonation token still does not
flow across threads, but for ASP.NET 2.0 applications you can configure
ASP.NET to flow the impersonation token to newly created threads.
Index of Guidelines
Input/Data Validation
If
you make unfounded assumptions about the type, length, format, or range
of input, your application is unlikely to be robust. Input validation
can become a security issue if an attacker discovers that you have made
unfounded assumptions. The attacker can then supply carefully crafted
input that compromises your application. The misplaced trust of user
input is one of the most common and serious vulnerabilities in Web
applications.
To help avoid input data validation vulnerabilities:
- Do not rely on ASP.NET request validation.
- Validate input for length, range, format, and type.
- Validate input from all sources like QueryString, cookies, and HTML controls.
- Do not rely on client-side validation.
- Avoid user-supplied file name and path input.
- Do not echo untrusted input.
- If you need to write out untrusted data, encode the output.
Do Not Rely on ASP.NET Request Validation
The
ASP.NET request validation feature performs basic input validation. Do
not rely on it. Use it as an extra precautionary measure in addition to
your own input validation. Only you can define what represents good
input for your application.
Request validation is enabled by default. You can see this by examining the validateRequest attribute, which is set to true on the <pages> element
in the Machine.config.comments file. Ensure that it is enabled for all
pages except those that need to accept a range of HTML elements. If you
need to disable it for a page, set the ValidateRequest attribute to false by using the @ Page directive.
Validate Input for Length, Range, Format, and Type
Do
not trust input. An attacker passing malicious input can attempt SQL
injection, cross-site scripting, and other injection attacks that aim
to exploit your application's vulnerabilities. Check for known good
data and constrain input by validating it for type, length, format, and
range. For Web form applications that obtain input through server
controls, use the ASP.NET validator controls, such as the RegularExpressionValidator, RangeValidator, and CustomValidator,
to validate and constrain input. Check all numeric fields for type and
range. If you are not using server controls, you can use regular
expressions and the Regex class, and you can validate numeric
ranges by converting the input value to an integer or double and then
performing a range check.
Validate Input from All Sources Like QueryString, Cookies, and HTML Controls
Most
Web applications accept input from various sources, including HTML
controls, server controls, query strings, and cookies. Validate input
from all of these sources to help prevent injection attacks. Use
regular expressions to help validate input. The following example shows
how to use the Regex class.
using System.Text.RegularExpressions ;
// Instance method:
Regex reg = new Regex(@"^[a-zA-Z'.\s]{1,40}$");
Response.Write(reg.IsMatch(Request.QueryString["Name"]));
// Static method:
if (!Regex.IsMatch(Request.QueryString["Name"],@"^[a-zA-Z'.\s]{1,40}$"))
{
// Name does not match expression
}
If you cannot cache your regular expression for frequent use, you should use the static IsMatch method where possible for performance reasons, to avoid unnecessary object creation.
Do Not Rely on Client-Side Validation
Do
not rely on client-side validation because it can be easily bypassed.
For example, a malicious user could disable your client-side script
routines by disabling JavaScript. Use client-side validation in
addition to server-side validation to reduce round trips to the server
and to improve the user experience.
Avoid User-Supplied File Name and Path Input
Where
possible, avoid writing code that accepts user-supplied file or path
input. Failure to do this can result in attackers coercing your
application into accessing arbitrary files and resources. If your
application must accept input file names, file paths, or URL paths, you
need to validate that the path is in the correct format and that it
points to a valid location within the context of your application.
File Names
Ensure
that file paths only refer to files within your application's virtual
directory hierarchy if that is appropriate. When checking file names,
obtain the full name of the file by using the System.IO.Path.GetFullPath method.
File Paths
If you use MapPath to map a supplied virtual path to a physical path on the server, use the overloaded Request.MapPath method that accepts a bool parameter so that you can prevent cross-application mapping. The following code example shows this technique.
try
{
string mappedPath = Request.MapPath( inputPath.Text,
Request.ApplicationPath, false);
}
catch (HttpException)
{
// Cross-application mapping attempted
}
The final false
parameter prevents cross-application mapping. This means that a user
cannot successfully supply a path that contains ".." to traverse
outside of your application's virtual directory hierarchy. Any attempt
to do this results in an exception of type HttpException.
Do Not Echo Untrusted Input
Do
not echo input back to the user without first validating and/or
encoding the data. Echoing input directly back to the user makes your
application susceptible to malicious input attacks, such as cross-site
scripting.
If You Need to Write Out Untrusted Data, Encode the Output
If
you write output that includes user input or data from a shared
database or a local file that you do not trust, encode it. Echoing
input directly back to the user makes your application vulnerable to
cross-site scripting attacks. Encoding the data ensures that it is
treated as literal text and not as script. You can use the HttpUtility.HtmlEncode method.
Similarly, if you write URLs that might contain unsafe characters
because they have been constructed from input data or data from a
shared database, use HttpUtilty.UrlEncode to make them safe.
Note Make sure that you encode data at the last possible opportunity before the data is returned to the client.
Additional Resources
For additional resources on input validation, see the following How Tos:
Authentication
Where
possible, you should use Windows authentication because this enables
you to use an existing identity store such as your corporate Active
Directory, it enables you to enforce strong password policies, you do
not need to build custom identity store management tools and passwords
are not transmitted over the network.
ASP.NET creates an Identity object that implements the System.Security.Principal.IIdentity interface to represent the authenticated user. Regardless of authentication method, you access the authenticated user through HttpContext.Current.User.
If
your application is configured for Windows authentication, Internet
Information Services (IIS) authenticates the user and the user's
Windows token is passed to ASP.NET. ASP.NET constructs a System.Security.Principal.WindowsIdentity object, which contains the user's token, places this inside a System.Security.Principal.WindowsPrincipal object, and then attaches this to the current Web request.
When
your application is configured for a non-Windows authentication
mechanism such as forms authentication, the Windows token passed by IIS
to ASP.NET is a token for the anonymous Internet user account
IUSR_MACHINENAME. With forms authentication, ASP.NET constructs a System.Web.Security.FormsIdentity object and places it inside a System.Security.Principal.GenericPrincipal object before attaching it to the current Web request.
This section provides guidance on forms authentication and Windows authentication:
- Forms authentication
- Windows authentication
Forms Authentication
To
protect forms authentication, you need to protect user-supplied
credentials, the credential store and the authentication ticket. To do
this, use the following guidelines:
- Use membership providers instead of custom authentication.
- Use SSL to protect credentials and authentication cookies.
- If you cannot use SSL, consider reducing session lifetime.
- Validate user login information.
- Do not store passwords directly in the user store.
- Enforce strong passwords.
- Protect access to your credential store.
- Do not persist authentication cookies.
- Restrict authentication tickets to HTTPS connections.
- Consider partitioning your site to restricted areas and public areas.
- Use unique cookie names and paths.
Use Membership Providers Instead of Custom Authentication
In
ASP.NET version 1.1, you had to implement custom logic for validating
user credentials, performing user management, and managing the
authentication ticket. In version 2.0, you can use the built-in
membership feature. The membership feature helps protect credentials,
can enforce strong passwords, and provides consistent APIs for user
validation and secure user management. The membership feature also
automatically creates the authentication ticket for you.
The
membership feature has built-in providers for user stores including SQL
Server, Active Directory, and Active Directory Application Mode (ADAM).
If you want to use an existing user store, such as a non-Active
Directory LDAP directory, or a user store on another platform, create a
custom membership provider inheriting from the MembershipProvider
abstract base class. By doing this, your application can still benefit
from using the standard membership features and API and login controls.
For more information, see How To: Use Membership in ASP.NET 2.0.
Use SSL to Protect Credentials and Authentication Cookies
Use
Secure Sockets Layer (SSL) to protect the authentication credentials
and authentication cookies passed between browser and server. By using
SSL, you prevent an attacker monitoring the network connection to
obtain authentication credentials and capturing the authentication
cookie to gain spoofed access to your application.
If You Cannot Use SSL, Consider Reducing Session Lifetime
If
you cannot use SSL, limit the cookie lifetime to reduce the time window
in which an attacker can use a captured cookie to gain access to your
application with a spoofed identity. The default timeout for an
authentication cookie is 30 minutes. Consider reducing this to 10
minutes as shown here.
<forms
timeout="00:10:00"
slidingExpiration="true"... />
The slidingExpiration="true"
setting ensures that the expiration period is reset after each Web
request. With the preceding configuration, this means that the cookie
only times out after a 10 minute period of inactivity.
If you are in a scenario where you are concerned about cookie hijacking, consider reducing the timeout and setting slidingExpiration="false".
If sliding expiration is turned off, the authentication cookie expires
after the timeout period whether or not the user is active. After the
timeout period, the user must re-authenticate.
Validate User Login Information
Validate
user login information including user names and passwords for type,
length, format, and range. Use regular expressions to constrain the
input at the server. If you are not using the SqlMembershipProvider
and need to develop your own queries to access your user store
database, do not use login details to dynamically construct SQL
statements because this makes your code susceptible to SQL injection.
Instead, validate the input and then use parameterized stored
procedures.
Also encode login details before echoing them back to the user's browser to prevent possible script injection. For example, use HtmlEncode as shown here.
Response.Write(HttpUtility.HtmlEncode("Welcome " + Request.Form["username"]));
Do Not Store Passwords Directly in the User Store
Do
not store user passwords either in plaintext or encrypted format.
Instead, store password hashes with salt. By storing your password with
hashes and salt, you help prevent an attacker that gains access to your
user store from obtaining the user passwords. If you use encryption,
you have the added problem of securing the encryption key. Use one of
the membership providers to help protect credentials in storage and
where possible, specify a hashed password format on your provider
configuration.
If you must implement your own user stores, store
one-way password hashes with salt. Generate the hash from a combination
of the password and a random salt value. Use an algorithm such as
SHA256. If your credential store is compromised, the salt value helps
to slow an attacker who is attempting to perform a dictionary attack.
This gives you additional time to detect and react to the compromise.
Enforce Strong Passwords
Ensure
that your passwords are complex enough to prevent users guessing other
users' passwords and to prevent successful dictionary attacks against
your user credential store.
By default, the ASP.NET membership providers enforce strong passwords. For example, the SqlMembershipProvider and the ActiveDirectoryMembership
providers ensure that passwords are at least seven characters in length
with at least one non-alphanumeric character. Ensure that your
membership provider configuration enforces passwords of at least this
strength. To configure the precise password complexity rules enforced
by your provider, you can set the following additional attributes:
- passwordStrengthRegularExpression. The default is "".
- minRequiredPasswordLength. The default is 7.
- minRequiredNonalphanumericCharacters. The default is 1.
Note The default values shown here apply to the SqlMembershipProvider and the ActiveDirectoryMembershipProvider. The ActiveDirectoryMembershipProvider also verifies passwords against the default domain password policy.
Protect Access to Your Credential Store
Ensure
only those accounts that require access are granted access to your
credential store. This helps to protect the credential store by
limiting access to it. For example, consider limiting access to only
your application's account. Ensure that the connection string used to
identify your credential store is encrypted.
Also consider
storing your credential database on a physically separate server from
your Web server. This makes it harder for an attacker to compromise
your credential store even if he or she manages to take control of your
Web server.
Do Not Persist Authentication Cookies
Do
not persist authentication cookies because they are stored in the
user's profile and can be stolen if an attacker gets physical access to
the user's computer. To ensure a non-persistent cookie, set the DisplayRememberMe property of the Login control to false. If you are not using the login controls, you can specify a non-persistent cookie when you call either the RedirectFromLoginPage or SetAuthCookie methods of the FormsAuthentication class having validated the user's credentials, as shown here.
public void Login_Click(object sender, EventArgs e)
{
// Is the user valid?
if (Membership.ValidateUser(userName.Text, password.Text))
{
// Parameter two set to false indicates non-persistent cookie
FormsAuthentication.RedirectFromLoginPage(username.Text, false);
}
else
{
Status.Text = "Invalid credentials. Please try again.";
}
}
Restrict Authentication Tickets to HTTPS Connections
Set the secure
property of the authentication cookie to ensure that browsers only send
authentication cookies over HTTPS connections. By using SSL, you
prevent an attacker from capturing the authentication cookie to gain
spoofed access to your application.
Set the secure property by using requireSSL="true" on the <forms> element as shown here.
<forms loginUrl="Secure\Login.aspx"
requireSSL="true" ... />
Consider Partitioning Your Site to Restricted Areas and Public Areas
To
avoid the performance overhead of using SSL across your entire site,
consider using a separate folder to help protect pages that require
authenticated access. Configure that folder in IIS to require SSL
access. Those pages that support anonymous access can safely be
accessed over HTTP connections.
Use Unique Cookie Names and Paths
Use unique name and path attribute values on the <forms>
element. By ensuring unique names, you prevent possible problems that
can occur when hosting multiple applications on the same server. For
example, if you do not use distinct names, it is possible for a user
who is authenticated in one application to make a request to another
application without being redirected to that application's logon page.
Additional Considerations
In addition to the preceding guidelines, consider the following.
- Set httpOnly on the authentication cookie. Set the httpOnlyCookies
attribute on the authentication cookie. Internet Explorer 6 Service
Pack 1 supports this attribute, which prevents client-side script from
accessing the cookie from the document.cookie property. The System.Net.Cookie class in .NET Framework version 2.0 supports an HttpOnly property. The HttpOnly property is always set to true by forms authentication.
Additional Resources
For additional resources on forms authentication, see the following How Tos:
Windows Authentication
If you use Windows authentication, consider the following guidelines:
- Choose Windows authentication when you can.
- Enforce strong password policies.
Choose Windows Authentication When You Can
When
possible, use Windows authentication to authenticate your users. By
using Windows authentication with Active Directory, you benefit from a
unified identity store, centralized account administration, enforceable
account and password policies, and strong authentication that avoids
sending passwords over the network.
Enforce Strong Password Policies
To
help ensure that users cannot guess one another's passwords and to help
prevent successful dictionary attacks in the event your password store
is compromised, enforce strong passwords through Active Directory
policy. To enforce a strong password policy:
- Set password length and complexity.
Strong passwords are eight or more characters and must include both
alphabetical and numeric characters. The default enforced by the ActiveDirectoryMembershipProvider
is seven characters with at least one non-alphanumeric character. If
you use this provider, the provider settings are checked first,
followed by the Active Directory settings. Users must supply passwords
that conform to the stronger of the two.
- Set password expiration. Passwords
that expire regularly reduce the likelihood that an old password can be
used for unauthorized access. Frequency of expiration is usually guided
by a company's security policy. You should define this in Active
Directory.
For more information on forms authentication, see How To: Use Windows Authentication in ASP.NET 2.0.
Authorization
You
control authorization administratively through URL and file
authorization and programmatically by performing identity and role
checks in code.
You also need to consider who you are
authorizing. Are you authorizing the application, the caller. or both?
Also, what are you authorizing access to? You could be authorizing
against:
- System resources. These include the file system and registry.
- Application resources. These include business logic and network resources, such as databases and Web services.
- User resources. These include resources such as customer records.
When implementing authorization logic, consider the following guidelines:
- Use URL authorization for page and directory access control.
- Configure ACLs on your Web site files.
- Use ASP.NET role manager for roles authorization.
- If your role lookup is expensive, consider role caching.
- Protect your authorization cookie.
Use URL Authorization for Page and Directory Access Control
Use
URL authorization to control which users and groups of users have
access to the application or to parts of the application. In ASP.NET
version 1.1, URL authorization only applies to file types that are
mapped by IIS to the ASP.NET ISAPI extension (Aspnet_isapi.dll). In
ASP.NET version 2.0 on Windows Server 2003, URL authorization protects
all files in a directory, even files that are not mapped to ASP.NET,
such as .html, .gif, and .jpg files.
When your application uses
Windows authentication, you can authorize access to Windows user and/or
Windows group accounts. To do so, you should configure your application
to use ASP.NET Role Manager. For more information, see the "Use ASP.NET
Role Manager for Roles Authorization" section in this topic.
You can use the <location>
tag to apply authorization settings to an individual file or directory.
The following example shows how you can apply authorization to a
specific file (page.aspx).
<location path="page.aspx" />
<authorization>
<allow users="DomainName\Bob, DomainName\Mary" />
<deny users="*" />
</authorization>
</location>
Configure ACLs on Your Web Site Files
You
need to configure the right access control lists (ACLs) for the right
identities on your Web site files so that IIS and also ASP.NET file
authorization control access to these files appropriately. You need to
grant access to the following identities:
- Your Web application identity. If you
are using a custom service account to run your ASP.NET application, you
can grant the appropriate permissions to the IIS metabase and to the
file system by running Aspnet_regiis.exe with the–ga switch.
- Your application's users.
ASP.NET file authorization performs access checks for file types mapped
by IIS to the ASP.NET ISAPI extension (Aspnet_isapi.dll). If you are
using Windows authentication, the authenticated user's Windows access
token (which may be IUSR_MACHINE for anonymous users) is checked
against the ACL attached to the requested ASP.NET file. If you are
using forms authentication, access is checked against IUSR_MACHINE.
File
authorization works automatically when using Windows authentication,
and there is no need to impersonate the original user. The FileAuthorizationModule
only performs access checks against the requested file. For example, if
you request Default.aspx and it contains an embedded user control
(Usercontrol.ascx), which in turn includes an image tag (pointing to
Image.gif), the FileAuthorizationModule performs an access
check for Default.aspx and Usercontrol.ascx, because these file types
are mapped by IIS to the ASP.NET ISAPI extension. The FileAuthorizationModule
does not perform a check for Image.gif, because this is a static file
handled internally by IIS. However, because access checks for static
files are performed by IIS, the authenticated user must still be
granted read permission to the file with an appropriately configured
ACL.
Use ASP.NET Role Manager for Roles Authorization
In
ASP.NET version 1.1, you had to create, manage, and look up roles for
the authenticated user by writing your own code. ASP.NET version 2.0
provides a new role manager feature that automatically performs this
work. Roles are accessed from the configured role store by the RoleManager
HTTP module by using the configured role provider. This occurs after
the user is authenticated but before URL authorization and file
authorization access checks occur and before programmatic role checks
can occur.
If your application needs role-based authorization, use the following guidelines:
If your user accounts are in Active Directory,
but you cannot use Windows authentication and must use forms
authentication, a good solution for roles management is to use the AuthorizationStoreRoleProvider with an AzMan policy store in ADAM.
Note The AuthorizationStoreRoleProvider
does not directly support Authorization Manager business logic such as
operations and tasks. To do this, you must use P/Invoke and call the
Authorization Manager API directly.
For more information, see How To: Use Role Manager in ASP.NET 2.0.
If Your Role Lookup Is Expensive, Consider Role Caching
If
the performance overhead of role lookup is too great, perhaps because
of slow data access or a large number of roles, consider caching roles
in the roles cookie by setting the cacheRolesInCookie attribute to true in the Web.config file as shown here.
<roleManager enabled="true"
cacheRolesInCookie="true"
... >
</roleManager>
When
role checks are performed, the roles cookie is checked before calling
the role provider to check the list of roles within the data source.
This improves performance. The cookie is dynamically updated to cache
the most recently validated role names. If the role information for a
user is too long to store in a cookie, ASP.NET stores only the most
recently used role information in the cookie and then it looks up
additional role information in the data source as required. The most
recently referred to roles end up being cached in the cookie.
Protect Your Authorization Cookie
You
should protect roles data in the authorization cookie to stop users
from modifying the list of roles to which they belong, and to stop
intruders from gaining information about the roles used by your
application. You protect the authorization cookie by appropriately
configuring the <roleManager> element as follows:
- Set the cookieProtection attribute to All.
This ensures that the role names in the cookie are digitally signed and
encrypted to prevent unauthorized viewing and modification of the role
data.
- Set the cookieSlidingExpiration attribute to true and the cookieTimeout
attribute to an integer number of minutes (for example, 10 to 20 or
fewer). The cookie expires when the timeout expires and must be renewed.
- Set the cookieRequireSSL attribute to true
to specify that the authorization cookie with the role information
should only be returned to the server over HTTPS connections.
- Set the createPersistentCookie attribute to false to ensure that the roles cookie is session-based and not a persistent cookie.
- If you cannot use SSL, consider reducing the cookieTimeout to 10 minutes. If you are concerned with cookie hijacking, consider setting the cookieSlidingExpiration attribute to false. This causes the cookie to expire after the fixed timeout period and must be renewed.
The following example shows a <roleManager> element configured to protect the authorization cookie.
<roleManager enabled="true"
cacheRolesInCookie="true"
cookieName=".ASPROLES"
cookieTimeout="30"
cookiePath="/"
cookieRequireSSL="true"
cookieSlidingExpiration="true"
cookieProtection="All"
createPersistentCookie="false">
</roleManager>
Additional Resources
For additional resources on Windows authentication, see the following How Tos:
Code Access Security
Code
access security is a resource constraint model designed to restrict the
types of system resource that code can access and the types of
privileged operation that the code can perform. These restrictions are
independent of the user who calls the code or the user account under
which the code runs. ASP.NET code access security policy is defined by
trust levels.
When using code access security with ASP.NET, consider the following guidelines:
- Consider code access security for partial trust applications.
- Choose a trust level that does not exceed your application's requirements.
- Create a custom trust policy if your application needs additional permissions.
- Use Medium trust in shared hosting environments.
Consider Code Access Security for Partial Trust Applications
If
your application calls only managed code, you can use different code
access security trust levels to incrementally limit your exposure to
security attacks and provide extra degrees of application isolation.
You should consider code access security particularly if you need
application isolation in shared hosting environments.
If you
do not plan on running your application at a partial trust level, code
access security presents no additional design or development
considerations because at Full trust, code access permission demands
will succeed. An application's trust level is determined by the
<trust> element as shown here.
<trust level="Full|High|Medium|Low|Minimal" />
Choose a Trust Level That Does Not Exceed Your Application's Requirements
Choose
a trust level that does not provide additional permissions beyond what
your application requires. By doing this, you follow the principle of
least privilege and constrain your application as much as possible. To
select the most appropriate trust level, identify the precise set of
code access security permissions that your application requires. You
can do this by manually reviewing your code or by using the Permissions
Calculator tool (Permcalc.exe). Evaluate whether the permissions
required for your application match those provided by any of the
standard trust levels. To see the permissions each trust level
provides, examine each trust level policy file in the
%windir%\Microsoft.NET\Framework\{version}\CONFIG directory, beginning
with the High trust policy file web_hightrust.config.
If your
application requires fewer code access security permissions than those
provided by the High trust level, move on to consider Medium trust.
Repeat the process, moving from Medium to Low to Minimal, and keep
evaluating the partial trust levels until you reach an exact match to
your application's requirements or until your application's required
permissions slightly exceed a partial trust level. If your application
needs more permissions than are granted by one level but requires fewer
that are provided by the next level, consider creating a custom trust
policy.
For more information, see How To: Use Code Access Security in ASP.NET 2.0.
Create a Custom Trust Policy if Your Application Needs Additional Permissions
If
your application requires additional permissions beyond those provided
at a particular trust level, but it does not need the additional
permissions provided by the next trust level, create a custom trust
policy file. This avoids granting your application unnecessary
permissions.
To create a custom policy, copy the trust level
policy file that most closely matches your application's permission
requirements to create a new custom policy file. Locate this file in
the standard policy file directory, which is
%Windir%\Microsoft.NET\Framework\{Version}\CONFIG. Then add the
required additional permissions to the custom policy file and configure
your application to run using the custom policy.
For more information, see How To: Use Code Access Security in ASP.NET 2.0.
Use Medium Trust in Shared Hosting Environments
In
ASP.NET version 1.1, Web applications configured for Medium trust could
not access SQL Server databases. In ASP.NET version 2.0, SQL Server
database access is available to Medium trust applications because the
SQL Server managed data provider no longer demands Full trust. If you
need to isolate multiple applications on a shared server from one
another and from shared system resources, use Medium trust. To enforce
Medium trust for all Web applications on a server, use the following
configuration in the machine-level Web.config file.
<location allowOverride="false">
<system.web>
<trust level="Medium" originUrl="" />
</system.web>
</location>
By setting allowOverride="false", an individual developer is unable to override the Medium trust policy setting in his or her application's Web.config file.
Medium
trust provides application isolation because it restricts file system
access to the application's own virtual directory hierarchy, and it
limits access to HTTP Web resources to a defined address or set of
addresses specified by the originUrl attribute on the <trust>
element. It also prevents access to shared system resources such as the
Windows event log and registry. Additionally, Medium trust applications
cannot use reflection and cannot access non-SQL Server OLE DB data
sources.
For more information, see How To: Use Medium Trust in ASP.NET 2.0.
Data Access
When building the data access layer of your application, consider the following guidelines:
- Encrypt your connection strings.
- Use least-privileged accounts for database access.
- Use Windows authentication where possible.
- If you use Windows authentication, use a trusted service account.
- If you cannot use a domain account, consider mirrored accounts.
- When using SQL authentication, use strong passwords.
- When using SQL authentication, protect credentials over the network.
- When using SQL authentication, protect credentials in configuration files.
- Validate untrusted input passed to your data access methods.
- When constructing SQL queries, use type safe SQL parameters.
- Avoid dynamic queries that accept user input.
Encrypt your connection strings
In
ASP.NET version 1.1, you could encrypt connection strings by using
DPAPI encryption, although you had to write managed code to wrap the
calls to DPAPI. This approach also presented problems in Web farms
because of machine affinity. In ASP.NET version 2.0, you can use a
protected configuration provider, such as DPAPI or RSA, which is easier
to use in Web farms. You do not have to write code because you use the
Aspnet_regiis.exe utility.
In ASP.NET version 2.0 applications, place your database connection strings inside the <connectionStrings>
element of the Web.config file and then encrypt that element by using
the Aspnet_regiis utility. In a Web farm, use the RSA-protected
configuration provider because RSA keys can be exported and imported
across servers.
For more information, see How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI and How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA.
Use Least-Privileged Accounts for Database Access
Your
application should connect to the database by using a least-privileged
account. This limits the damage that can be done in the event of a SQL
injection attack or in the event of an attacker obtaining your
account's credentials. With Windows authentication, use a
least-privileged account with limited operating system permissions, and
limited ability to access Windows resources. Regardless of
authentication mechanism, restrict the account's permissions in the
database.
Use the following pattern to limit permissions in the database:
- Create a SQL Server login for the account.
- Map the login to a database user in the required database.
- Place the database user in a database role.
- Grant the database role limited permissions to only those stored procedures or tables that your application needs to access.
Ideally,
provide no direct table access and limit access to selected stored
procedures only. If you must grant table access, grant the minimum
access that the application requires. For example, do not grant update
access if read access is sufficient.
By using a database role,
you avoid granting permissions directly to the database user. This
isolates you from potential changes to the database user name. For
example, if you change the database user name, you can simply add the
new user to the database role and remove the existing one.
Use Windows Authentication Where Possible
Prefer
Windows authentication when connecting to SQL Server or other databases
that support it. Windows authentication offers a number of security
advantages in comparison to SQL authentication:
- Accounts are centralized and managed by your Active Directory or local authority store.
- Strong password policies can be controlled and enforced by your domain or local security policy.
- Passwords are not transmitted over the network.
- User IDs and passwords are not specified in database connection strings.
For more information, see How To: Connect to SQL Server Using Windows Authentication in ASP.NET 2.0.
If You Use Windows Authentication, Use a Trusted Service Account
If
you use Windows authentication, use a trusted service account to access
the database when possible. This is usually your application's process
account. By using a single trusted service account, your application
benefits from connection pooling; this provides greater scalability.
Also, account administration and authorization within the database is
simplified. If you need per-user authorization in the database or need
to use operating system auditing to track the activity of individual
users, you need to use impersonation and delegation and access the
database using the caller's identity. This approach has limited
scalability because it prevents the efficient use of connection pooling.
If You Cannot Use a Domain Account, Consider Mirrored Accounts
If
you cannot use domain accounts because of domain trust or firewall
restrictions, consider using mirrored service accounts instead. With
this approach, you still use Windows authentication, but you create two
local accounts with the same name and password on the Web server and
database server. You configure your application to run using the local
account created on the Web server and create a SQL login for the local
account on the database server. With this approach, you must ensure
that the passwords of the two accounts remain in synchronization.
For more information, see How To: Connect to SQL Server Using SQL Authentication in ASP.NET 2.0.
When Using SQL Authentication, Use Strong Passwords
If
you use SQL Server authentication, make sure you use a least-privileged
account with a strong password to prevent an attacker guessing your
account's password. A strong password should be at least seven
characters in length and contain a combination of alphabetic, numeric,
and special characters.
Avoid using blank passwords and the sa account as shown in the following connection string.
SqlConnectionString = "Server=YourServer\Instance; Database=YourDatabase; uid=sa; pwd=;"
Use least-privileged accounts with a strong password, such as the following.
SqlConnectionString= "Server=YourServer\Instance;
Database=YourDatabase;
uid=YourStrongAccount;
pwd=YourStrongP@ssw0rd;"
When Using SQL Authentication, Protect Credentials Over the Network
If
your application is not located in a physically secure isolated data
center, you should use Internet Protocol Security (IPSec) or SSL to
create an encrypted communication channel between the Web server and
database server if your use SQL authentication. Failure to do this
means that credentials can be easily captured with a network monitor.
When you connect to SQL Server with SQL authentication, the credentials
are not encrypted prior to transmission across the network.
Use
SSL when you need granular channel protection for a particular
application instead of for all applications and services running on a
computer. If you want to protect all the IP traffic between Web and
database server, use IPSec. You can also use IPSec to restrict which
computers can communicate with one another. For example, you can help
protect a database server by establishing a policy that permits
requests only from a trusted client computer, such as an application or
Web server. You can also restrict communication to specific IP
protocols and TCP/UDP ports.
When Using SQL Authentication, Protect Credentials in Configuration Files
To protect credentials in configuration files, encrypt them. Place your database connection strings inside the <connectionStrings>
element of the Web.config file and then encrypt that element by using
the Aspnet_regiis utility. You can use DPAPI or RSA encryption. Use RSA
in Web farms because you can easily export and import RSA keys across
servers. Protecting connection strings is particularly important for
connection strings that use SQL authentication because they contain
clear text user IDs and passwords.
Note You
should also encrypt connection strings if you use Windows
authentication. Although this form of connection string does not
contain credentials, you should aim to keep server and database names
private.
For more information, see How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI and How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA.
Validate Untrusted Input Passed to Your Data Access Methods
If
your data access methods receive input parameters from outside the
trust boundary of your data access code, make sure you validate them
for type, length, format, and range. You can use regular expressions
for text input and perform type and range checks on numeric data. If
you do not do this, your data access code is potentially susceptible to
SQL injection.
Only omit input parameter validation in your
data access methods if you know for certain that data can only be
supplied by trusted code, such as your application's business logic,
which you know has thoroughly validated the data passed to it.
Note Avoid storing encoded data; instead, encode the data as close as possible to the output.
When Constructing SQL Queries, Use Type Safe SQL Parameters
Use
type safe parameters when constructing SQL queries to avoid possible
SQL injection attacks that can occur with unfiltered input. You can use
type safe parameters with stored procedures and with dynamic SQL
statements. Parameters are treated as literal values by the database
and not as executable code. Parameters are also checked for type and
length.
The following code shows how to use type safe parameters with the SqlParameterCollection when calling a stored procedure.
using System.Data;
using System.Data.SqlClient;
using (SqlConnection connection = new SqlConnection(connectionString))
{
DataSet userDataset = new DataSet();
SqlDataAdapter myCommand = new SqlDataAdapter(LoginStoredProcedure", connection);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
myCommand.SelectCommand.Parameters.Add("@au_id", SqlDbType.VarChar, 11);
myCommand.SelectCommand.Parameters["@au_id"].Value = SSN.Text;
myCommand.Fill(userDataset);
}
In
the preceding code example, the input value cannot be longer than 11
characters. If the data does not conform to the type or length defined
by the parameter, the SqlParameter class throws an exception. For more information about preventing SQL injection, see How To: Protect from SQL Injection in ASP.NET.
Avoid Dynamic Queries That Accept User Input
Avoid
constructing SQL queries in code that include user input; instead,
prefer parameterized store procedures that use type safe SQL
parameters. If you construct queries dynamically using user input, your
code is susceptible to SQL injection. For example, avoid the following
style of code.
// Use dynamic SQL
SqlDataAdapter myCommand = new SqlDataAdapter(
"SELECT au_lname, au_fname FROM authors WHERE au_id = '" +
SSN.Text + "'", myConnection);
If
a malicious user supplies "' ; DROP DATABASE pubs --'" for the SSN.Text
field, the code inserts the user's malicious input and generates the
following query.
SELECT au_lname, au_fname FROM authors WHERE au_id = ''; DROP DATABASE pubs --'
The
; (semicolon) character tells SQL that this is the end of the current
statement, which is then followed by the following malicious SQL code,
which in this example drops the authors table.
Additional Resources
For additional resources on connecting to SQL Server, see the following How Tos:
Exception Management
Correct
exception handling in your Web pages prevents sensitive exception
details from being revealed to the user, improves application
robustness, and helps avoid leaving your application in an inconsistent
state in the event of errors. Consider the following guidelines:
- Use structured exception handling.
- Do not reveal exception details to the client.
- Use a global error handler to catch unhandled exceptions.
Use Structured Exception Handling
Use
structured exception handling and catch exception conditions. Doing
this improves robustness and avoids leaving your application in an
inconsistent state that may lead to information disclosure. It also
helps protect your application from denial of service attacks. Use finally
blocks to ensure that resources are cleaned up and closed even in the
event of an exception condition. Decide how to propagate exceptions
internally in your application and give special consideration to what
occurs at the application boundary.
Do Not Reveal Exception Details to the Client
When
exceptions occur, return concise error messages to the client and log
specific details on the server. Do not reveal internal system or
application details, such as stack traces, SQL statement fragments, and
table or database names to the client. Ensure that this type of
information is not allowed to propagate to the end user or beyond your
current trust boundary. A malicious user could use system-level
diagnostic information to learn about your application and probe for
weaknesses to exploit in future attacks.
Prevent detailed error messages from displaying by setting the mode attribute of the <customErrors> element to On, so that all callers receive filtered exception information. Do not use mode="Off"
because this allows detailed error pages intended for application
developers that contain system-level information to be returned to the
client.
You should also use the <customErrors>
section of the Web.config file as shown in the following code example
to specify a default error page to display, along with other required
error pages for specific HTTP response codes that indicate errors.
<customErrors mode="On" defaultRedirect="ErrDefault.aspx">
<error statusCode="401" redirect="ErrUnauthorized.aspx" />
<error statusCode="404" redirect="ErrPageNotFound.aspx" />
<error statusCode="500" redirect="ErrServer.htm" />
</customErrors>
The defaultRedirect
attribute allows you to use a custom error page for your application,
which. for example, might include support contact details. Use these
application-wide error pages to provide user-friendly responses for
errors that are not caught in a structured event handling.
Use a Global Error Handler to Catch Unhandled Exceptions
Define
an application-level global error handler in Global.asax to catch any
exceptions that are not handled in code. Do this to avoid accidentally
returning detailed error information to the client. You should log all
exceptions in the event log to record them for tracking and later
analysis. Use code similar to the following.
<%@ Application Language="C#" %>
<%@ Import Namespace="System.Diagnostics" %>
<script language="C#" runat="server">
void Application_Error(object sender, EventArgs e)
{
//get reference to the source of the exception chain
Exception ex = Server.GetLastError().GetBaseException();
// log the details of the exception and page state to the
// event log.
EventLog.WriteEntry("My Web Application",
"MESSAGE: " + ex.Message +
"\nSOURCE: " + ex.Source,
EventLogEntryType.Error);
// Optional e-mail or other notification here...
}
</script>
Impersonation/Delegation
By
default, ASP.NET Web applications do not impersonate, although in some
scenarios, you need to use impersonation to perform operations or
access resources using the authenticated user's identity. If you use
impersonation, consider the following guidelines:
- Know your tradeoffs with impersonation.
- Avoid calling LogonUser.
- Avoid programmatic impersonation where possible.
- If you need to impersonate, consider threading issues.
- If you need to impersonate, clean up appropriately.
- Avoid losing impersonation tokens.
Know Your Tradeoffs with Impersonation
Be
aware that impersonation prevents the efficient use of connection
pooling if you access downstream databases by using the impersonated
identity. This impacts the ability of your application to scale. Also,
using impersonation can introduce other security vulnerabilities,
particularly in multi-threaded applications, such as ASP.NET Web
applications.
You might need impersonation if you need to:
- Flow the original caller's security context to
the middle tier and/or data tier of your Web application to support
fine-grained (per-user) authorization.
- Flow the original caller's security context to the downstream tiers to support operating system level auditing.
- Access a particular network resource by using a specific identity.
Avoid Calling LogonUser
Avoid writing code that calls the LogonUser API to create impersonation tokens because this forces you to store user names and passwords on your Web server.
Instead,
use a unique application pool with a specific process identity if you
need a specific identity to access downstream resources. If you need
multiple identities to access a range of downstream resources and
services, use Windows Server 2003 protocol transition and the new WindowsIdentity
constructor; this allows you to create a Windows token that is given
only an account's user principal name (UPN). To access a network
resource, you need to delegate-level token. To get this token type,
your server needs to be configured as trusted for delegation in Active
Directory.
The following code shows how to use this constructor to obtain a Windows token for a given user.
using System;
using System.Security.Principal;
public void ConstructToken(string upn, out WindowsPrincipal p)
{
WindowsIdentity id = new WindowsIdentity(upn);
p = new WindowsPrincipal(id);
}
Note An
account's UPN is guaranteed to be unique within a forest. Frequently,
the UPN is the user's e-mail address, but it does not have to be. A
user always has a UPN and by default, it is userlogonname@fullyqualifieddomainname. If you are logged into a domain, you can find your UPN name by running whoami /upn from a command window.
For more information, see How To: Use Protocol Transition and Constrained Delegation with ASP.NET 2.0.
Avoid Programmatic Impersonation Where Possible
If
programmatic impersonation is not done properly, it can introduce
security vulnerabilities. It is difficult to get it correct,
particularly in multithreaded applications. When possible, use
alternative approaches, such as a custom domain process identity for
resource access. You should avoid programmatic impersonation where
possible for the following reasons:
- It is easy to introduce errors because of thread switches where the thread impersonation token is not propagated across threads.
- Some programmatic techniques require you to store credentials which should be avoided.
- Some
programmatic techniques require you to grant additional privileges to
your process account, which you should avoid. For example, you must
grant your process account "Act as part of the operating system" if you
call LogonUser on Windows Server 2000 or to obtain an impersonate-level token on Windows Server 2003 when you use the new WindowsIdentity constructor that generates a token from a user principal name.
- If
exceptions occur while impersonating, it is possible for malicious code
higher in the call stack to run using the impersonated identity. This
can present security issues, particularly if you impersonate a highly
privileged account.
If You Need to Impersonate, Consider Threading Issues
Losing
an impersonated security context because of thread switches is a common
vulnerability. The common language runtime (CLR) automatically
propagates impersonation tokens to threads that you create using any of
the managed threading techniques, such as Thread.Start, an asynchronous delegate, or QueueUserWorkItem.
However, it is easy to drop the thread impersonation token if you use
COM interop with components that have incompatible threading models or
if you use unmanaged techniques to create new threads such as the Win32
CreateThread API.
If You Need to Impersonate, Clean Up Appropriately
If you must use programmatic impersonation, use structured exception handling and put the impersonation code inside try blocks. Use a catch block to handle exceptions and use a finally block to ensure that the impersonation is reverted as shown here.
using System.Security.Principal;
...
WindowsIdentity winIdentity = new WindowsIdentity("username@domainName");
WindowsImpersonationContext ctx = winIdentity.Impersonate();
try
{
// Do work
}
catch(Exception ex)
{
// Stop impersonating
ctx.Undo();
}
finally
{
// Stop impersonating
ctx.Undo();
}
By using a finally
block, you ensure that the impersonation token is removed from the
current thread whether an exception is generated or not. Also be aware
that if your code fails to catch exceptions, a malicious user could use
exception filters to execute code that runs under the impersonated
security context. This is particularly serious if your code
impersonates a privileged account. If your code does not catch the
exception, exception filters higher in the call stack are executed
before code in your finally block is executed.
Note Exception filters are supported by Microsoft Intermediate Language (MSIL) and Visual Basic .NET.
For more information, see How To: Use Impersonation and Delegation in ASP.NET 2.0.
Avoid Losing Impersonation Tokens
In
.NET Framework 1.1, impersonation tokens did not automatically flow to
newly created threads. This situation could lead to security
vulnerabilities because new threads assume the security context of the
process. In ASP.NET 2.0 applications you can now change this default
behavior by configuring the ASPNET.config file in the %Windir%Microsoft.NET\Framework\{Version} directory.
If you need to flow the impersonation token to new threads, set the enabled attribute to true on the alwaysFlowImpersonationPolicy element in the ASPNET.config file, as shown in the following example.
....
<configuration>
<runtime>
<alwaysFlowImpersonationPolicy enabled="true"/>
</runtime>
</configuration>
....
If you need to prevent impersonation tokens from being passed to new threads programmatically, you can use the ExecutionContext.SuppressFlow method.
Parameter Manipulation
Parameters,
such as those found in form fields, query strings, view state, and
cookies, can be manipulated by attackers who usually intend to gain
access to restricted pages or trick the application into performing an
unauthorized operation.
The following recommendations help you avoid parameter manipulation vulnerabilities:
- Do not make security decisions based on parameters accessible on the client-side.
- Validate all input parameters.
- Avoid storing sensitive data in ViewState.
- Encrypt ViewState if it must contain sensitive data.
Do Not Make Security Decisions Based on Parameters Accessible on the Client-Side
Do
not trust input parameters, especially when they are used to make
security decisions at the server. Also, do not use clear text
parameters for any form of sensitive data. Instead, store sensitive
data on the server in a session store and use a session token to
reference the items in the store.
Validate All Input Parameters
Validate all input parameters that come from form fields, query strings, cookies, and HTTP headers. The System.Text.RegularExpressions.Regex
class helps validate input parameters. For example, the following code
shows how to use this class to validate a name passed through a query
string parameter. The same technique can be used to validate other
forms of input parameter, such as from cookies or form fields. For
example, to validate a cookie parameter, use Request.Cookies instead of Request.QueryString.
using System.Text.RegularExpressions;
...
private void Page_Load(object sender, System.EventArgs e)
{
// Name must contain between 1 and 40 alphanumeric characters
// together with (optionally) special characters such as apostrophes
// for names such as D'Angelo.
if (!Regex.IsMatch(Request.QueryString["name"], @"^[a-zA-Z'`-´\s]{1,40}$"))
throw new Exception("Invalid name parameter");
// Use individual regular expressions to validate all other
// query string parameters.
...
}
For more information, see How To: Protect from Injection Attacks in ASP.NET and How To: Use Regular Expressions to Constrain Input in ASP.NET.
Avoid Storing Sensitive Data in ViewState
Avoid
storing sensitive data in ViewState. ViewState is not designed for
sensitive data, and protecting it with encryption adds to performance
overhead. If you need to manage sensitive data, maintain it on the
server; for example, maintain it in session state.
If your
ViewState does contain sensitive data, you should consider protecting
it against eavesdropping by enabling ViewState encryption as described
in the next section.
Encrypt ViewState if It Must Contain Sensitive Data
Using
SSL protects ViewState while it is passed over the network between
server and browser, but it does not stop it being viewed and modified
on the user's computer.
To prevent ViewState from being viewed
on the user's computer (and over the network), use ASP.NET ViewState
encryption. Do not use it if your ViewState does not contain sensitive
data because encryption significantly adds to the size of the ViewState
and this impacts performance.
For more information about protecting ViewState, see How To: Configure the Machine Key in ASP.NET 2.0.
Additional Considerations
In addition to the preceding guidelines, consider the following.
- Use Page.ViewStateUserKey to counter one-click attacks.
If you authenticate your callers and use ViewState, set the Page.ViewStateUserKey property in the Page_Init event handler to prevent one-click attacks.
void Page_Init (object sender, EventArgs e) {
ViewStateUserKey = Session.SessionID;
}
Set the property to a value you know is unique to each user, such as a session ID, user name, or user identifier.
A one-click attack occurs when an attacker creates a Web page
(.htm or .aspx) that contains a hidden form field named __VIEWSTATE
that is already filled with ViewState data. The ViewState can be
generated from a page that the attacker had previously created, such as
a shopping cart page with 100 items. The attacker lures an unsuspecting
user into browsing to the page, and then the attacker causes the page
to be sent to the server where the ViewState is valid. The server has
no way of knowing that the ViewState originated from the attacker.
ViewState validation and HMACs do not counter this attack because the
ViewState is valid and the page is executed under the security context
of the user.
By setting the ViewStateUserKey property, when the
attacker browses to a page to create the ViewState, the property is
initialized to his or her name. When the legitimate user submits the
page to the server, it is initialized with the attacker's name. As a
result, the ViewState HMAC check fails and an exception is generated.
Note This
attack is usually not an issue for anonymously browsed pages (where no
user name is available), because this type of page should make no
sensitive transactions.
Sensitive Data
Sensitive
data includes application configuration details (for example,
connection strings and service account credentials) and
application-specific data (for example, customer credit card numbers).
The following recommendations help to reduce risk when you handle
sensitive data:
- Avoid plaintext passwords in configuration files.
- Use platform features to manage keys where possible
- Do not pass sensitive data from page to page
- Protect sensitive data over the wire
- Do not cache sensitive data
Avoid Plaintext Passwords in Configuration Files
The <sessionState> and <identity> elements in the Machine.config and Web.config files have userName and password attributes. If you store credentials in these sections, encrypt them by using one of the protected configuration providers.
For more information, see How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI and How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA.
Use Platform Features to Manage Keys Where Possible
Use
platform features where possible to avoid managing keys yourself. For
example, by using DPAPI, the encryption key is derived from an
account's password, so Windows handles this for you.
Do Not Pass Sensitive Data from Page to Page
Avoid
using any of the client-side state management options, such as
ViewState, cookies, query strings, or hidden form-field variables, to
store sensitive data. The data can be tampered with and viewed in clear
text. Use server-side state management options, such as a SQL Server
database to help protect data exchange.
Protect Sensitive Data Over the Wire
Consider
where items of sensitive data, such as credentials and
application-specific data, are transmitted over a network link. If you
need to send sensitive data between the Web server and browser,
consider using SSL. If you need to protect server-to-server
communication, such as between your Web server and database, consider
IPSec or SSL.
Do Not Cache Sensitive Data
If
your page contains data that is sensitive, such as a password, credit
card number, or account status, the page should not be cached. Output
caching is off by default.
Session Management
There
are two main factors that you should consider to help protect session
state. First, ensure that the session token cannot be used to gain
access to sensitive pages where secure operations are performed or to
gain access to sensitive items of data. Second, if the session data
contains sensitive items, you must protect the session data, including
the session store.
The following recommendations help you build secure session management:
- Do not rely on client-side state management options.
- Protect your out-of-process state service.
- Protect SQL Server session state.
Do Not Rely on Client-Side State Management Options
Avoid
using any of the client-side state management options, such as view
state, cookies, query strings, or hidden form fields, to store
sensitive data. The information can be tampered with or seen in clear
text. Use server-side state management options, for example, a
database, to store sensitive data.
Protect Your Out-of-Process State Service
If you use mode=StateServer on the <sessionState> element, use the following recommendations to help secure session state:
- Use a least-privileged account to run the state service.
By default, the state service runs using the Network Service local,
least-privileged account. You should not need to change this
configuration
- Protect the channel. If the state service
is located on a remote server and you are concerned about the network
eavesdropping threat, protect the channel to the remote state store by
using IPSec to ensure the user state remains private and unaltered.
- Consider changing the default port.
The ASP.NET state service listens on port 42424. To avoid using this
default, well-known port, you can change the port by editing the
following registry key:
HKLM\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters
The port number is defined by the Port
named value. If you change the port number in the registry, for
example, to 45678, you must also change the connection string on the
<sessionState> element, as follows:
stateConnectionString="tcpip=127.0.0.1:45678"
- Encrypt the state connection string.
Because this element contains service connection details, you should
encrypt it by using one of the protected configuration providers. For
more information, see How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI and How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA.
Protect SQL Server Session State
If you use mode="SQLServer" on the <sessionState> element, use the following recommendations to help protect session state:
- Use Windows authentication to the database. This
means that passwords are not sent over the network to the database. It
also means you do not have to store user names and passwords in the
database connection string.
- Encrypt the <sessionState> section.
Because this element contains database connection details, you should
encrypt it by using one of the protected configuration providers. For
more information, see How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI and How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA.
- Limit the application's login in the database.
Restrict the login in the database so that it can only be used to
access the necessary state tables and the stored procedures used by
ASP.NET to query the database. To do this, create a SQL Server login
for your ASP.NET process account, and then map the login to a database
user in the state store database. Assign the database user to a
database role and grant execute permissions for the stored procedures
that are provided with the ASPState database.
- Protect the channel. To
protect sensitive session state over the network between the Web server
and remote state store, protect the channel to the two servers using
IPSec or SSL. This provides privacy and integrity for the session state
data across the network. If you use SSL, you must install a server
certificate on the database server.
Auditing and Logging
You
should audit and log activity across the tiers of your application.
Using logs, you can detect suspicious-looking activity. This frequently
provides early indications of a full-blown attack and the logs help
address the repudiation threat where users deny their actions. Log
files may be required in legal proceedings to prove the wrongdoing of
individuals. Generally, auditing is considered most authoritative if
the audits are generated at the precise time of resource access and by
the same routines that access the resource.
When implementing auditing and logging in ASP.NET applications:
- Use health monitoring to log and audit events.
- Instrument for user management events.
- Instrument for unusual activity.
- Instrument for significant business operations.
- Consider using an application-specific event source.
- Protect audit and log files.
Use Health Monitoring to Log and Audit Events
ASP.NET
version 2.0 introduces a health monitoring feature that you should use
to log and audit events. By default, health monitoring is enabled for
ASP.NET version 2.0 applications and all Web infrastructure error
events (inheriting from System.Web.Management.WebErrorEvent) and all audit failure events (inheriting from System.Web.Management.WebFailureAuditEvent) are written to the event log. The default configuration is defined in the <healthMonitoring>
element in the machine-level Web.config file. To audit additional
security related events, you create custom event types by deriving from
one of the built-in types.
The health monitoring feature has built-in providers that allow you to log events in an e-mail message (SimpleMailWebEventProvider, TemplatedMailWebEventProvider), to SQL Server (SqlWebEventProvider), to the event log (EventLogWebEventProvider), as ASP.NET trace output (TraceWebEventProvider), or to the Windows Management Instrumentation (WMI) Web event provider (WMIWebEventProvider).
You can configure health monitoring in the machine or application
Web.config file to modify the events that are logged and the way in
which they are logged.
For more information, see How To: Use Health Monitoring in ASP.NET 2.0.
Instrument for User Management Events
Instrument
your application and monitor user management events such as password
resets, password changes, account lockout, user registration, and
authentication events. Doing this helps you to detect and react to
potentially suspicious behavior. It also enables you to gather
operations data; for example, to track who is accessing your
application and when user account passwords need to be reset.
By default, ASP.NET health monitoring records all WebFailureAuditEvents, which ASP.NET raises when user authentication fails in forms authentication with the membership system. ASP.NET also raises a WebFailureAuditEvent when authorization is not granted to a resource protected by file authorization and URL authorization.
For more information about auditing password changes and account lockout events, see How To: Instrument ASP.NET 2.0 Applications for Security.
Instrument for Unusual Activity
Instrument
your application and monitor events that might indicate unusual or
suspicious activity. This enables you to detect and react to potential
problems as early as possible. Unusual activity might be indicated by:
- Replays of old authentication tickets.
- To many login attempts over a specific period of time.
Replays of old authentication tickets automatically raise an ExpiredTicketFailure event. To raise an event for too many login attempts, if you are using the membership system and the Login controls, you can write an event handler for the LoginError event of the Login control. In the handler, call Membership.GetUser(Login1.UserName).IsLockedOut
to determine if there have been too many login attempts for the user.
You can then raise a custom event to indicate that the account has been
locked out.
For more information about auditing password changes and account lockout events, see How To: Instrument ASP.NET 2.0 Applications for Security.
Instrument for Significant Business Operations
Use
ASP.NET health monitoring to track significant business operations. For
example, instrument your application to record access to particularly
sensitive methods and business logic. To do this, create a custom event
class that inherits from System.Web.Management.WebSuccessAuditEvent or System.Web.Management.WebFailureAuditEvent and raise that event in the appropriate methods. For more information see How To: Instrument ASP.NET 2.0 Applications for Security.
Consider Using an Application-specific Event Source
By default, the EventLogWebEventProvider
writes to the application event log by using an event source named
"ASP.NET xxxxxx", where xxxxxx is the .NET Framework version number.
This is not configurable. If you want events generated by the ASP.NET
health monitoring feature to use an application-specific event source,
you must create a custom event provider (inherit from EventLogWebEventProvider) and override the ProcessEvent method to write to the event log (by calling EventLog.Write) using the desired event source.
When
using a custom event source, you need to create the event source at
installation time when administrator privileges are available. If you
are unable to create new event sources at installation time, and you
are in deployment, the administrator should manually create new event
source entry beneath the following registry key
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\<LogName>
Note You
should not grant write permission to the ASP.NET process account (or
any impersonated account if your application uses impersonation) on the
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\ registry
key. If you allow write access to this key and the account is
compromised, the attacker can modify any log-related setting, including
access control to the log, for any log on the system.
Protect Audit and Log Files
Protect
audit and log files using Windows ACLs and restrict access to the log
files. If you log events to SQL Server or to some custom event sink,
use appropriate access controls to limit access to the event data. For
example, grant write access to the account or accounts used by your
application, grant full control to administrators, and read-only access
to operators.
This makes it more difficult for attackers to
tamper with log files to cover their tracks. Minimize the number of
individuals who can manipulate the log files. Authorize access only to
highly trusted accounts such as administrators.
Additional Considerations
In addition to the preceding guidelines, consider the following measures for auditing and logging:
- Log application events on a separate, protected server. This helps to ensure that logs cannot be tampered by attackers.
- Assign appropriate permissions to the log files. Logs should be written by a process with write permission only. Logs should be read by users with administrative access.
- Log application events with sufficient details. Provide sufficient detail to permit reconstruction of system activity.
- Use performance counters for high volume, per-request events. This helps to minimize the impact on performance.
Deployment Considerations
To avoid introducing vulnerabilities when you deploy your application into a production environment:
- Use a least-privileged account for running ASP.NET applications.
- Encrypt configuration sections that store sensitive data.
- Consider your key storage location.
- Block protected file retrieval by using HttpForbiddenHandler.
- Configure the MachineKey to use the same keys on all servers in a Web farm.
- Lock configuration settings to enforce policy settings.
Use a Least-Privileged Account for Running ASP.NET Applications
On
IIS 5, ASP.NET Web applications run by default using the least
privileged ASPNET account. On IIS 6, they run inside an application
pool using the least privileged Network Service account. If you need to
use a custom service account to run your ASP.NET Web application,
create a least-privileged account. You might need to create a custom
service account to isolate your application from other applications on
the same server or to be able to audit each application separately.
To create a least privileged account
- Create a local or domain Windows account.
- Run the following Aspnet_regiis.exe command to assign the relevant ASP.NET permissions to the account:
aspnet_regiis.exe -ga machineName\userName
On Windows
Server 2003, running the Aspnet_regiis.exe -ga command will add the
account to the IIS_WPG group. The IIS_WPG group provides the Log on as a batch job user right and ensures that the necessary file system permissions are granted.
Note With
ASP.NET Beta 2, the Aspnet_regiis.exe–ga command does not add the
account to the IIS_WPG group. However, it will do so prior to the final
release of ASP.NET 2.0.
- Use the Local Security Policy tool to grant the Windows account the Deny logon locally user right. This reduces the privileges of the account and prevents anyone logging onto Windows locally with the account.
- Use
IIS Manager to create an application pool running under the new
account's identity and assign your ASP.NET application to the pool.
For more information, see How To: Create a Service Account for an ASP.NET 2.0 Application.
Encrypt Configuration Sections That Store Sensitive Data
In
ASP.NET version 1.1, you could use the Aspnet_setreg.exe tool to
encrypt certain elements in the Web.config and Machine.config files
that contain credentials. This tool replaces the credentials in the
configuration file with a pointer to a registry key that holds the
DPAPI encrypted data. In ASP.NET version 2.0, you can use either the
DPAPI or RSA protected configuration providers to encrypt specific
sections that contain sensitive data. The sections that usually contain
sensitive information that you need to encrypt include <appSettings>, <connectionStrings>, <identity>, and <sessionState>.
To encrypt the <connectionStrings>
section by using the DPAPI provider with the machine-key store (the
default configuration), run the following command from a command window:
aspnet_regiis -pe "connectionStrings" -app "/MachineDPAPI" -prov "DataProtectionConfigurationProvider"
- -pe: Specifies the configuration section to encrypt.
- -app:
Specifies your Web application's virtual path. If your application is
nested, you need to specify the nested path from the root directory;
for example, "/test/aspnet/MachineDPAPI".
- -prov: Specifies the provider name.
If
you need to encrypt configuration file data on multiple servers in a
Web farm, you should use RSA protected configuration provider because
of the ease with which you can export RSA key containers. For more
information, see How To: Encrypt Configuration Sections in ASP.NET 2.0 Using DPAPI and How To: Encrypt Configuration Sections in ASP.NET 2.0 Using RSA.
Consider Your Key Storage Location
The
DPAPI and RSA protected configuration providers used to encrypt
sensitive data in configuration files can use either machine stores or
user stores for key storage. You can either store the key in the
machine store and create an ACL for your specific application identity
or store the key in a user store. In the latter case, you need to load
the user account's profile to access the key.
Use machine-level key storage when:
- Your application runs on its own dedicated server with no other applications.
- You
have multiple applications on the same server that run using the same
identity and you want those applications to be able to share sensitive
information and the same encryption key.
Use user-level
key storage if you run your application in a shared hosting environment
and you want to ensure that your application's sensitive data is not
accessible to other applications on the server. In this scenario, each
application should have a separate identity so they all have their own
individual and private key stores.
Block Protected File Retrieval by Using HttpForbiddenHandler
To prevent protected files being downloaded over HTTP, map them to the ASP.NET HttpForbiddenHandler. HTTP handlers are located in the machine-level Web.config file beneath the <httpHandlers> element. HTTP handlers are responsible for processing Web requests for specific file extensions.
Many well-known file extensions are already mapped to System.Web.HttpForbiddenHandler. For .NET Framework resources, if you do not use a particular file extension, map the extension to System.Web.HttpForbiddenHandler in the machine-level Web.config file, as shown here.
<add verb="*" path="*.asmx" type="System.Web.HttpForbiddenHandler" />
This example assumes that your Web server does not host Web services, so the .asmx file extension is mapped to System.Web.HttpForbiddenHandler.
If a client requests a path that ends with .asmx, ASP.NET returns a
message that says, "This type of page is not served." If your
application uses other files with extensions not already mapped, also
map these to System.Web.HttpForbiddenHandler.
Configure the MachineKey to Use the Same Keys on All Servers in a Web Farm
If
you deploy your application in a Web farm, you must ensure that the
configuration files on each server share hashing and encryption keys.
These are used by ASP.NET to protect ViewState and forms authentication
tickets. Manually generated, common keys are required because you
cannot guarantee which server will handle successive requests.
With manually generated key values, the <machineKey> settings should be similar to the following example.
<machineKey validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B4
4EA7DD4374267A75D7AD972A119482D15A4127461DB1DC347C1A63AE5F1CCFAACFF1B72A7F0A281B"
decryptionKey="261F793EB53B761503AC445E0CA28DA44AA9B3CF06263B77"
decryption="3DES"
validation="SHA1" />
For more information about how to manually generate keys, see How To: Configure MachineKey in ASP.NET 2.0.
Lock Configuration Settings to Enforce Policy Settings
To
prevent individual application's from being able to override
configuration settings with their own Web.config files, apply
configuration settings centrally in the machine-level Web.config file
located in the following directory:
%Windir%\Microsoft.NET\Framework\{version}\CONFIG.
To lock the configuration settings for all Web applications on a Web server, place the configuration settings inside a <system.web> element nested within a <location> element and set the allowOverride attribute to false.
The following example enforces the use of Windows authentication for all Web applications on the server.
<location allowOverride="false">
<system.web>
<authentication mode="Windows"/>
</system.web>
</location>
If you need to apply and lock settings for a specific Web application, use the path attribute on the <location> element to identify the Web application as shown here.
<location path="Default Web Site/VDirName">
<system.web>
<authentication mode="Windows"/>
<identity impersonate="false"/>
</system.web>
</location>
If you specify the path, it must be fully qualified and include the Web site name and virtual directory name.
Communication Security
When
passing sensitive data, such as credentials or application-specific
data, across networks, you need to consider the security of the
communication channel:
- Consider SSL vs. IPSec.
- Optimize pages that use SSL.
Consider SSL vs. IPSec
If
your servers are not inside a physically secure data center where the
network eavesdropping threat is considered insignificant, you need to
use an encrypted communication channel to protect data sent between
servers. SSL and IPSec can both be used to help protect communication
between servers by encrypting traffic. Use SSL when you need granular
channel protection for a particular application instead of for all
applications and services running on a computer.
Use IPSec to
help protect the communication channel between two servers and to
restrict which computers can communicate with one another. For example,
you can help protect a database server by establishing a policy that
permits requests only from a trusted client computer such as an
application or Web server. You can also restrict communication to
specific IP protocols and TCP/UDP ports.
Optimize Pages That Use SSL
Using
SSL is expensive. Use SSL only for pages that require it. This includes
pages that contain or capture sensitive data, such as pages that accept
credit card numbers and passwords. Use SSL only if the following
conditions are true:
- You want to encrypt the page data.
- You want to guarantee that the server to which you send the data is the server that you expect.
For pages where you must use SSL, follow these guidelines:
- Make the page size as small as possible.
- Avoid
using graphics that have large file sizes. If you use graphics, use
graphics that have smaller file sizes and resolution. Or, use graphics
from a site that is not secure. However, when you use graphics from a
site that is not secure, Web browsers display a dialog box that asks
the user if the user wants to display the content from the site that is
not secure.
Companion Guidance
The
following companion guidance is in the sequence that is referenced by
this document. This is useful if you want to print the How To documents
and refer to them in order.
Additional Resources
Feedback
Provide feedback by using either a Wiki or e-mail:
We are particularly interested in feedback regarding the following:
- Technical issues specific to recommendations
- Usefulness and usability issues
Technical Support
Technical
support for the Microsoft products and technologies referenced in this
guidance is provided by Microsoft Support Services. For product support
information, please visit the Microsoft Product Support Web site at http://support.microsoft.com.
Community and Newsgroups
Community support is provided in the forums and newsgroups:
To
get the most benefit, find the newsgroup that corresponds to your
technology or problem. For example, if you have a problem with ASP.NET
security features, you would use the ASP.NET Security forum.
Contributors and Reviewers
- External Contributors and Reviewers:
Eric Marvets, Dunn Training and Consulting; Jason Taylor, Security
Innovation; Rudolph Araujo, Foundstone Professional Services
- Microsoft Product Group: Stefan Schackow
- Microsoft Services and PSS Contributors and Reviewers: Adam Semel, Tom Christian, Wade Mascia, Microsoft Corporation
- Test team:
Larry Brader, Microsoft Corporation; Nadupalli Venkata Surya Sateesh,
Sivanthapatham Shanmugasundaram, Infosys Technologies Ltd.
- Edit team: Nelly Delgado, Microsoft Corporation; Tina Burden McGrayne, TinaTech Inc.
- Release Management: Sanjeev Garg, Microsoft Corporation.