patterns & practices Developer Center J.D. Meier, Alex Mackman, Blaine Wastell, Prashant Bansode, Andy Wigley
Microsoft Corporation
May 2005
Applies To
- ASP.NET version 1.1
- ASP.NET version 2.0
Summary
This
How To shows how you can validate input to protect your application
from injection attacks. Performing input validation is essential
because almost all application-level attacks contain malicious input.
You
should validate all input, including form fields, query string
parameters, and cookies to protect your application against malicious
command injection. Assume all input to your Web application is
malicious, and make sure that you use server validation for all sources
of input. Use client-side validation to reduce round trips to the
server and to improve the user experience, but do not rely on it
because it is easily bypassed.
To validate input, define
acceptable input for each application input field. A proven practice is
to constrain input for length, range, format, and type. Use the list of
acceptable characters to define valid input, instead of the list of
unacceptable characters. Using the list of unacceptable characters is
impractical because it is very difficult to anticipate all possible
variations of bad input.
When you need to accept a range of HTML
characters, make sure that you HTML-encode the data to make it safe
prior to displaying it as output.
Contents
Objectives
Overview
Summary of Steps
Step 1. Use ASP.NET Request Validation
Step 2. Constrain Input
Step 3. Encode Unsafe Output
Step 4. Use Command Parameters for SQL Queries
Step 5. Verify that ASP.NET Errors Are Not Returned to the Client
Additional Resources
Objectives
- Constrain input for length, range, format, and type.
- Apply ASP.NET request validation during development to identify injection attacks.
- Constrain input by using ASP.NET validator controls.
- Encode unsafe output.
- Help prevent SQL injection by using command parameters.
- Prevent detailed error information from returning to the client.
Overview
You
need to validate all untrusted input to your application. You should
assume that any input from users is malicious. User input to your Web
application includes form fields, query strings, client-side cookies,
and browser environment values such as user agent strings and IP
addresses.
Weak input validation is a common vulnerability
that could allow your application to be exploited by a number of
injection attacks. The following are common types of attacks that
exploit weak or missing input validation:
- SQL injection. If you generate dynamic
SQL queries based on user input, an attacker could inject malicious SQL
commands that can be executed by the database.
- Cross-site scripting.
Cross-site scripting (XSS) attacks exploit vulnerabilities in Web page
validation by injecting client-side script code. This code is
subsequently sent to an unsuspecting user's computer and executed on
the browser. Because the browser downloads the script code from a
trusted site, the browser has no way of determining whether the code is
legitimate.
- Unauthorized file access. If your code
accepts input from a caller, a malicious user could potentially
manipulate your code's file operations, such as accessing a file they
should not access or exploiting your code by injecting bad data.
Note Injection
attacks work over HTTP and HTTPS Secure Socket Layer (SSL) connections.
Encryption provides no defense.
The general approach
for input validation is summarized here. You should apply this approach
to any input that comes from the network, such as text boxes and other
forms field input, query string parameters, cookies, server variables,
and Web method parameters. Note that the strategy is to first allow
only good input and then deny bad input. This is because you can easily
define good input for your application, but you cannot realistically
anticipate the format for all malicious input.
Check for valid input as follows:
- Constrain: Check for known good data by
validating the type, length, format, and range. To constrain input from
server controls, use the ASP.NET validator controls. To constrain input
from other sources, use regular expressions and custom validation.
- Reject: Check for any known bad data and reject bad input.
- Sanitize:
Sometimes you also need to sanitize input and make potentially
malicious input safe. For example, if your application supports
free-format input fields, such as comment fields, you might want to
permit certain safe HTML elements, such as <b> and <i>, and eliminate any other HTML elements.
Summary of Steps
To protect your ASP.NET application from injection attacks, perform the following steps:
- Step 1. Use ASP.NET request validation.
- Step 2. Constrain input.
- Step 3. Encode unsafe output.
- Step 4. Use command parameters for SQL queries.
- Step 5. Verify that ASP.NET errors are not returned to the client.
The next sections describe each of these.
Step 1. Use ASP.NET Request Validation
By
default, ASP.NET versions 1.1 and 2.0 request validation detects any
HTML elements and reserved characters in data posted to the server.
This helps prevent users from inserting script into your application.
Request validation checks all input data against a hard-coded list of
potentially dangerous values. If a match occurs, it throws an exception
of type HttpRequestValidationException.
You can disable request validation in your Web.config application configuration file by adding a <pages> element with validateRequest="false" or on an individual page by setting ValidateRequest="false" on the @ Pages element.
If
you need to disable request validation, you should disable it only on
the affected page. An example of this is when you have a page with a
free-format text field that accepts HTML-formatted input.
Confirm that ASP.NET Request Validation Is Enabled in Machine.config
Request
validation is enabled by ASP.NET by default. You can see the following
default setting in the Machine.config.comments file.
<pages validateRequest="true" ... />
Confirm
that you have not disabled request validation by overriding the default
settings in your server's Machine.config file or your application's
Web.config file.
Test ASP.NET Request Validation
You can test the effects of request validation. To do this, create an ASP.NET page that disables request validation by setting ValidateRequest="false", as follows.
<%@ Language="C#" ValidateRequest="false" %>
<html>
<script runat="server">
void btnSubmit_Click(Object sender, EventArgs e)
{
// If ValidateRequest is false, then 'hello' is displayed
// If ValidateRequest is true, then ASP.NET returns an exception
Response.Write(txtString.Text);
}
</script>
<body>
<form id="form1" runat="server">
<asp:TextBox id="txtString" runat="server"
Text="<script>alert('hello');</script>" />
<asp:Button id="btnSubmit" runat="server" OnClick="btnSubmit_Click"
Text="Submit" />
</form>
</body>
</html>
When you run the page, "Hello" is displayed in a message box because the script in txtString is passed through and rendered as client-side script in your browser.
If you set ValidateRequest="true" or remove the ValidateRequest page attribute, ASP.NET request validation rejects the script input and produces an error similar to the following.
A potentially dangerous Request.Form value was detected from the client (txtString="<script>alert('hello...").
Note Do
not rely on ASP.NET request validation. Treat it as an extra
precautionary measure in addition to your own input validation.
Step 2. Constrain Input
To constrain input, follow these guidelines:
- Use server-side input validation. Do
not rely on client-side validation because it is easily bypassed. Use
client-side validation in addition to server-side validation to reduce
round trips to the server and to improve the user experience.
- Validate length, range, format and type. Make sure that any input meets your guidelines for known good input.
- Use strong data typing. Assign numeric values to numeric data types such as Integer or Double. Assign string values to string data types. Assign dates to the DateTime data type.
For
Web form applications that obtain input through server controls, use
the ASP.NET validator controls to constrain the input. For other
sources of input data, such as query strings, cookies, and HTTP
headers, constrain input by using the Regex class from the System.Text.RegularExpressions namespace.
Explicitly Check Input from Form Fields
To constrain form field input received through server controls, you can use the following ASP.NET validator controls:
- RegularExpressionValidator. Use this control to constrain text input.
- RangeValidator. Use this control to check the ranges of numeric, currency, date, and string input.
- CustomValidator. Use this control for custom validation, such as ensuring that a date is in the future or in the past.
To validate form field input received through HTML input controls, perform validation in server-side code and use the Regex class to help constrain text input. The following sections describe how to constrain a variety of common input types.
Validating Text Fields
- To validate text fields, such as names, addresses, and tax identification numbers, use regular expressions to do the following:
- Constrain the acceptable range of input characters.
- Apply
formatting rules. For example, pattern-based fields, such as tax
identification numbers, ZIP Codes, or postal codes, require specific
patterns of input characters.
- Check lengths.
Using a RegularExpressionValidator
To use a RegularExpressionValidator, set the ControlToValidate, ValidationExpression, and ErrorMessage properties to appropriate values as shown in the following example.
<form id="WebForm" method="post" runat="server">
<asp:TextBox id="txtName" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator id="nameRegex" runat="server"
ControlToValidate="txtName"
ValidationExpression="^[a-zA-Z'.\s]{1,40}$"
ErrorMessage="Invalid name">
</asp:regularexpressionvalidator>
</form>
The
regular expression used in the preceding code example limits an input
name field to alphabetic characters (lowercase and uppercase), space
characters, the single apostrophe for names such as O'Dell, and the
period. In addition, the field length is constrained to 40 characters.
Note The RegularExpressionValidator
control automatically adds a caret (^) and dollar sign ($) as
delimiters to the beginning and end of expressions if you have not
added them yourself. You should add them to all of your regular
expressions as good practice. Enclosing the expression in the
delimiters ensures that the expression consists of the desired content
and nothing else.
Using the Regex Class
If
you are not using server controls (which means you cannot use the
validator controls), or you need to validate input from sources other
than form fields (such as from query string parameters or cookies), you
can use a Regex class.
To use the Regex class
- Add a using statement to reference the System.Text.RegularExpressions namespace.
- Ensure that the regular expression is contained in the ^ and $ anchor characters (beginning of string, end of string).
- Call the IsMatch method of the Regex class, as shown in the following code example.
// Instance method:
Regex reg = new Regex(@"^[a-zA-Z'.\s]{1,40}$");
Response.Write(reg.IsMatch(txtName.Text));
// Static method:
if (!Regex.IsMatch(txtName.Text,@"^[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.
Validating Numeric Fields
In
most cases, numeric fields should be checked for type and range. To
validate the type and range of a numeric input field that uses a server
control, you can use a RangeValidator control. The RangeValidator supports currency, date, integer, double, and string data types.
To use a RangeValidator, set the ControlToValidate, Type, MinimumValue, MaximumValue, and ErrorMessage properties to appropriate values as shown in the following example.
<asp:RangeValidator
ID="RangeValidator1"
Runat="server"
ErrorMessage="Invalid range. Number must be between 0 and 255."
ControlToValidate="rangeInput"
MaximumValue="255"
MinimumValue="0" Type="Integer" />
If
you are not using a server control, you can validate a numeric range by
converting the input value to an integer and then performing a range
check. For example, to validate that an integer is valid, convert the
input value to a variable of type System.Int32 by using the new Int32.TryParse method (introduced in the Microsoft .NET Framework version 2.0). This method returns false if the type conversion fails.
Int32 i;
if (Int32.TryParse(txtInput.Text, out i) == false)
{
// Conversion failed
}
If you are using an earlier version of the .NET Framework, you can use Int32.Parse or Convert.ToInt32 inside a try/catch block and handle any exceptions of type FormatException that are thrown if the input value is not the correct type.
The following code shows how to perform a type and range check for an integer entered through an HTML text input control.
<%@ Page Language="C#" %>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
if (Request.RequestType == "POST")
{
int i;
if (Int32.TryParse(Request.Form["integerTxt"], out i) == true)
{
// TryParse returns true if the conversion succeeds
if ((0 <= i && i <= 255) == true)
{
Response.Write("Input data is valid.");
}
else
Response.Write("Input data is out of range");
}
else
Response.Write("Input data is not an integer");
}
}
</script>
<html>
<body>
<form id="form1" action="NumericInput.aspx" method="post">
<div>
Enter an integer between 0 and 255:
<input name="integerTxt" type="text" />
<input name="Submit" type="submit" value="submit" />
</div>
</form>
</body>
</html>
Validating Date Fields
You
need to validate that date fields are of the correct type. In most
cases, you also need to check them for range, for example to validate
that they are in the future or past. If you use a server control to
capture an input date, and if you also need to validate that a date
falls within a specific range, you can use a RangeValidator control with its Type field set to Date.
This control lets you specify a range by using constant date values. If
you need to validate a date range based on today's date, for example to
validate that a date is in the future or the past, you can use a CustomValidator control.
To use a CustomValidator control to validate a date, set the ControlToValidate and ErrorMessage properties and the OnServerValidate event to point to a custom method containing your validation logic. The following sample .aspx page code shows this approach.
<%@ Page Language="C#" %>
<script runat="server">
void ValidateDateInFuture(object source, ServerValidateEventArgs args)
{
DateTime dt;
// Check for valid date and that the date is in the future
if ((DateTime.TryParse(args.Value, out dt) == false) ||
(dt <= DateTime.Today))
{
args.IsValid = false;
}
}
</script>
<html>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" Runat="server"
Text="Future Date:"></asp:Label>
<asp:TextBox ID="futureDatetxt" Runat="server"></asp:TextBox>
<asp:CustomValidator
ID="CustomValidator1" Runat="server"
ErrorMessage="Invalid date. Enter a date in the future."
ControlToValidate="futureDatetxt"
OnServerValidate="ValidateDateInFuture">
</asp:CustomValidator>
<br />
<asp:Button ID="submitBtn" Runat="server" Text="Submit" />
</div>
</form>
</body>
</html>
Note The preceding code uses DateTime.TryParse, which is new to .NET Framework 2.0.
Sanitizing Free-Text Fields
To sanitize
input, you make untrusted input safe by preventing it from being
treated as code. For example, if your application handles user input
that it cannot constrain or reads data from a shared database, you
might need to sanitize the data or make the output safe when you write
it on your page. Sanitize data prior to output by using HttpUtility.HtmlEncode.
Allowing Restricted HTML Input
If
your application needs to accept a range of HTML elements—for example
through a rich text input field such as a comments field—turn off
ASP.NET request validation and create a filter that allows only the
HTML elements that you want your application to accept. A common
practice is to restrict formatting to safe HTML elements such as <b> (bold) and <i>
(italic). Before writing the data, HTML-encode it. This makes any
malicious script safe by causing it to be handled as text, not as
executable code.
To allow restricted HTML input
- Disable ASP.NET request validation by the adding the ValidateRequest="false" attribute to the @ Page directive.
- Encode the string input with the HtmlEncode method.
- Use a StringBuilder and call its Replace method to selectively remove the encoding on the HTML elements that you want to permit.
The following .aspx page code shows this approach. The page disables ASP.NET request validation by setting ValidateRequest="false". It HTML-encodes the input and selectively allows the <b> and <i> HTML elements to support simple text formatting.
<%@ Page Language="C#" ValidateRequest="false"%>
<script runat="server">
void submitBtn_Click(object sender, EventArgs e)
{
// Encode the string input
StringBuilder sb = new StringBuilder(
HttpUtility.HtmlEncode(htmlInputTxt.Text));
// Selectively allow and <i>
sb.Replace("<b>", "<b>");
sb.Replace("</b>", "");
sb.Replace("<i>", "<i>");
sb.Replace("</i>", "");
Response.Write(sb.ToString());
}
</script>
<html>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="htmlInputTxt" Runat="server"
TextMode="MultiLine" Width="318px"
Height="168px"></asp:TextBox>
<asp:Button ID="submitBtn" Runat="server"
Text="Submit" OnClick="submitBtn_Click" />
</div>
</form>
</body>
</html>
Validate Query String Values
Validate
query string values for length, range, format, and type. You usually do
this by using a combination of regular expressions to:
- Constrain the input values.
- Set explicit range checks.
- Specify
the explicit type checks performed by converting the input value to its
equivalent .NET Framework type and handling any ensuing conversion
errors.
The following code example shows how to use the Regex class to validate a name string passed on a query string.
void Page_Load(object sender, EventArgs e)
{
if (!System.Text.RegularExpressions.Regex.IsMatch(
Request.QueryString["Name"], @"^[a-zA-Z'.\s]{1,40}$"))
Response.Write("Invalid name parameter");
else
Response.Write("Name is " + Request.QueryString["Name"]);
}
Validate Cookie Values
Values
maintained in cookies, such as query string parameters, can easily be
manipulated by a client. Validate cookie values in the same way as you
would for query string parameters. Validate them for length, range,
format, and type.
Validate File and URL Paths
If
your application has to 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. Failure to do this can result in attackers persuading your
application into accessing arbitrary files and resources.
Validating File Paths
To
prevent a malicious user manipulating your code's file operations,
avoid writing code that accepts user-supplied file or path input. For
example:
- If you must accept file names as input, use the full name of the file by using System.IO.Path.GetFileName.
- If you must accept file paths as input, use the full file path by using System.IO.Path.GetFullPath.
Using MapPath to Prevent Cross Application Mapping
If you use MapPath to map a supplied virtual path to a physical path on the server, use the overload of Request.MapPath 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.
If you use server controls, you can use the Control.MapPathSecure method to retrieve the physical path to which the virtual path is mapped. Control.MapPathSecure uses code access security and throws an HttpException if the server control does not have permissions to read the resulting mapped file. For more information, see Control.MapPathSecure in the .NET Framework SDK documentation.
Using Code Access Security to Restrict File I/O
An
administrator can restrict an application's file I/O to its own virtual
directory hierarchy by configuring the application to run with Medium
trust. In this event, .NET code access security ensures that no file
access is permitted outside of the application's virtual directory
hierarchy.
You configure an application to run with Medium trust by setting the <trust> element in Web.config or Machine.config.
Validating URLs
You can filter for a valid URL format using a regular expression, such as the following.
^(?:http|https|ftp)://[a-zA-Z0-9\.\-]+(?:\:\d{1,5})?(?:[A-Za-z0-9\.\;\:\@\&\=\+\$\,\?/]|%u[0-9A-Fa-f]{4}|%[0-9A-Fa-f]{2})*$
This
constrains the input, but it does not validate whether the URL is valid
in terms of the application boundaries. You should check whether the
target is valid in the context of your application. For example, does
it point to an authorized server that you expect your application to
communicate with?
Step 3. Encode Unsafe Output
If you write text output to a Web page, encode it using HttpUtility.HtmlEncode. Do this if the text came from user input, a database, or a local file.
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.
Avoid the
mistake of encoding the data early. Make sure you encode at the last
possible opportunity before the data is displayed to the client.
Use HtmlEncode to Encode Unsafe Output
The HtmlEncode
method replaces characters that have special meaning in HTML to HTML
variables that represent those characters. For example, < is
replaced with < and " is replaced with ".
Encoded data does not cause the browser to execute code. Instead, the
data is rendered as harmless text, and the tags are not interpreted as
HTML.
To illustrate the use of HtmlEncode, the following page accepts input from the user and allows potentially unsafe HTML characters by setting ValidateRequest="false". Before writing the input back to the user, the code calls HttpUtility.HtmlEncode on the supplied input text. This renders any potentially unsafe HTML as harmless text.
<%@ Page Language="C#" ValidateRequest="false" %>
<script runat="server">
void submitBtn_Click(object sender, EventArgs e)
{
Response.Write(HttpUtility.HtmlEncode(inputTxt.Text));
}
</script>
<html >
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="inputTxt" Runat="server"
TextMode="MultiLine" Width="382px" Height="152px">
</asp:TextBox>
<asp:Button ID="submitBtn" Runat="server" Text="Submit"
OnClick="submitBtn_Click" />
</div>
</form>
</body>
</html>
To
see the effect of the HTML encoding, place the preceding page in a
virtual directory, browse to it, enter some HTML code in the input text
box, and click Submit. For example, the following input is rendered as text.
Run script and say hello <script>alert('hello');</script>
It produces the following safe output.
Run script and say hello <script>alert('hello');</script>
If you remove the call to HtmlEncode
and simply write back the input, the browser executes the script and
displays a message box. Malicious script could pose a significant
threat.
Use UrlEncode to Encode Unsafe URLs
If you need to write URLs that are based on input that you do not fully trust, use HttpUtility.UrlEncode to encode the URL string.
HttpUtility.UrlEncode( urlString );
Step 4. Use Command Parameters for SQL Queries
To help prevent SQL injection, use command parameters for SQL queries. The Parameters collection provides type checking and length validation. If you use the Parameters
collection, input is treated as a literal value and SQL does not treat
it as executable code. An additional benefit of using the Parameters collection is that you can enforce type and length checks. Values outside of the range trigger an exception.
Use Parameters Collection When You Call a Stored Procedure
The following code fragment illustrates the use of the Parameters collection when calling a stored procedure.
SqlDataAdapter myCommand = new SqlDataAdapter("AuthorLogin",
myConnection);
myCommand.SelectCommand.CommandType = CommandType.StoredProcedure;
SqlParameter parm = myCommand.SelectCommand.Parameters.Add(
"@LoginId", SqlDbType.VarChar, 11);
parm.Value = Login.Text;
Use Parameters Collection When Building Your SQL Statements
If you cannot use stored procedures, you can still use parameters, as shown in the following code fragment.
SqlDataAdapter myCommand = new SqlDataAdapter(
"SELECT au_lname, au_fname FROM Authors WHERE au_id = @au_id", myConnection);
SQLParameter parm = myCommand.SelectCommand.Parameters.Add(
"@au_id" ,SqlDbType.VarChar, 11);
Parm.Value = Login.Text;
For more information about how to prevent SQL injection, see How To: Protect From SQL Injection in ASP.NET.
Step 5. Verify that ASP.NET Errors Are Not Returned to the Client
You can use the <customErrors>
element to configure custom, generic error messages that should be
returned to the client in the event of an application exception
condition.
Make sure that the mode attribute is set to "remoteOnly" in the web.config file as shown in the following example.
<customErrors mode="remoteOnly" />
After
installing an ASP.NET application, you can configure the setting to
point to your custom error page as shown in the following example.
<customErrors mode="On" defaultRedirect="YourErrorPage.htm" />
Additional Resources
For more information about how to protect your application from injection attacks, see the following documents:
Feedback
Provide feedback by using either a Wiki or e-mail:
We are particularly interested in feedback regarding the following:
- Technical issues specific to our 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 support
information, please visit the Microsoft 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 should use the ASP.NET Security forum.
Contributors and Reviewers
- External Contributors and Reviewers:
Andy Eunson; Chris Ullman, Content Master; David Raphael,
Foundstone Professional Services, Rudolph Araujo, Foundstone
Professional Services; Manoranjan M. Paul
- Microsoft Consulting Services and PSS Contributors and Reviewers: Adam Semel, Nobuyuki Akama, Tom Christian, Wade Mascia
- Microsoft Product Group Contributors and Reviewers: Stefan Schackow, Vikas Malhotra, Microsoft Corporation
- MSDN Contributors and Reviewers: Kent Sharkey, Microsoft Corporation
- Microsoft IT Contributors and Reviewers: Eric Rachner, Shawn Veney (ACE Team), Microsoft Corporation
- Test team:
Larry Brader, Microsoft Corporation; Nadupalli Venkata Surya Sateesh,
Sivanthapatham Shanmugasundaram, Sameer Tarey, Infosys Technologies Ltd
- Edit team: Nelly Delgado, Microsoft Corporation; Tina Burden McGrayne, Linda Werner & Associates Inc
- Release Management: Sanjeev Garg, Satyam Computer Services