Return to HomePage



Note: This document is live on MSDN! ''See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag2/html/paght000004.asp

How To: Prevent Cross Site Scripting in ASP.NET



J.D. Meier, Alex Mackman, Blaine Wastell, Prashant Bansode, Andy Wigley
Microsoft Corporation
May 2005

Applies To

* ASP.NET 1.1
* ASP.NET 2.0

Summary

Cross-site scripting (XSS) attacks exploit vulnerabilities in Web page validation by injecting client-side script code. This How To describes how you can protect your ASP.NET applications from cross-site scripting attacks by using proper input validation and output encoding. It also describes a number of other protection mechanisms that you can use in addition to these two main countermeasures.

Contents

* Overview
* Summary of Steps
* Step 1: Check ASP.NET Request Validation is Enabled
* Step 2: Review ASP.NET Code that Generates HTML Output
* Step 3: Determine Whether the HTML Output Includes Input Parameters
* Step 4: Review Potentially Dangerous HTML Tags and Attributes
* Step 5: Evaluate Countermeasures
* Additional Considerations
* Summary
* Additional Resources

Overview

Cross-site scripting attacks exploit vulnerabilities in Web page validation by injecting client-side script code. The script code embeds itself in response data, which is sent back to an unsuspecting user. The user's browser then runs the script code.. Because the browser downloads the script code from a trusted site, the browser has no way of recognizing that the code is not legitimate, and Internet Explorer security zones provide no defense. Cross-site scripting attacks also work over HTTP and HTTPS (SSL) connections.
One of the most serious examples of a cross-site scripting attack occurs when an attacker writes script to retrieve the authentication cookie that provides access to a trusted site, and then posts the cookie to a Web address known to the attacker. This enables the attacker to spoof the legitimate user's identity and gain illicit access to the Web site.
Common vulnerabilities that make your Web application susceptible to cross-site scripting attacks include:
* Failing to constrain and validate input.
* Failing to encode output.
* Trusting data retrieved from a shared database.

Guidelines

The two main countermeasures to prevent cross-site scripting attacks are:
* Constrain input
* Encode output

Constrain Input

Start by assuming that all input is malicious and validate it for type, length, format, and range.
* To constrain input supplied through server controls, use ASP.NET validator controls such as RegularExpressionValidator and RangeValidator.
* To constrain input supplied through client-side HTML input controls or input from other sources like query strings or cookies, use regular expressions in your server side code by using the System.Text.RegularExpressions.Regex class.
* To validate types such as integers, doubles, dates and currency amounts, convert the input data to the equivalent .NET Framework data type and handle any resulting conversion errors.

For more information and examples of how to constrain input, see How To: Protect from Injection Attacks in ASP.NET.

Encode Output

Use the HttpUtility.HtmlEncode method to encode output if it contains input either from the user or from other sources such as databases. HtmlEncode replaces characters that have special meaning in HTML to HTML variables that represent those characters. For example, < is replaced with &lt and " is replaced with &quot. Encoded data does not cause the browser to execute code. Instead, the data is rendered as harmless HTML.
Similarly, use HttpUtility.UrlEncode to encode output ""URLs"" if they are constructed from input.

Summary of Steps

To prevent cross site scripting, perform the following steps:
* Step 1: Check ASP.NET Request Validation is Enabled
* Step 2: Review ASP.NET Code that Generates HTML Output
* Step 3: Determine Whether the HTML Output Includes Input Parameters
* Step 4: Review Potentially Dangerous HTML Tags and Attributes
* Step 5: Evaluate Countermeasures

Step 1: Check ASP.NET Request Validation is Enabled

Request validation is enabled by default in Machine.config. Ensure that request validation is currently enabled in your server's Machine.config and that your application doesn't override this setting in its Web.config. Check that validateRequest is set to true.

		 <system.web>
		   <pages buffer="true" validateRequest="true">
		 <system.web/>
	

You can also disable request validation on a page by page basis. Check that your pages do not disable this feature unless it is needed. It might be needed if your page contains a free format, rich text entry field designed to accept a range of HTML characters as input. For more information about how to safely handle this type of page, see Step 5, Evaluate Countermeasures.
To test that ASP.NET request validation is enabled
* 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>
	

* 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.
* Set ValidateRequest="true" or remove the ValidateRequest page attribute and browse to the page again. Verify that the following error message is displayed.

		 A potentially dangerous Request.Form value was detected from the client (txtString="<script>alert('hello...").
	

This indicates that ASP.NET request validation is enforced and has rejected the input because it includes potentially dangerous HTML characters.

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: Review ASP.NET Code that Generates HTML Output


ASP.NET writes HTML to the output in two ways:

		 Response.Write
		 <% =
	

Search your pages to locate where HTML and URL output is sent back to the client.

Step 3: Determine Whether the HTML Output Includes Input Parameters

Analyze your design and your page code to determine whether the output includes any input parameters. These parameters can come from a variety of sources. The following list includes common input sources:

* Form Fields
		 Response.Write(name.Text);
		 Response.Write(Request.Form["name"]);
		 Query Strings
		 Response.Write(Request.QueryString["name"]);
	
* Request Object
		 Response.Write("username")
	
* Databases / Data Access Methods. Be particularly wary of data read from a database if it is shared by other applications.
		 [SqlDataReader] reader = [cmd.ExecuteReader();]
		 [Response.Write(reader.GetString(1));]
	
* Cookies Collection
		 Response.Write(
		 Request.Cookies["name"].Values["name"]);
	
* Session and Application Variables
		 Response.Write(Session["name"]);
		 Response.Write(Application["name"]);
	

In addition to source code analysis, you can also perform a simple test by typing text such as "XYZ" in form fields and testing the output. If the browser displays "XYZ" or if you see "XYZ" when you view the source of the HTML, then your Web application is vulnerable to cross site scripting.
To see something more dynamic, inject <script>alert('hello');</script> through an input field. This technique might not work in all cases because it depends on how the input is used to generate the output.

Step 4: Review Potentially Dangerous HTML Tags and Attributes

If you dynamically create HTML tags and construct tag attributes with potentially unsafe input, make sure you HTML-encode the tag attributes before writing them out.
The following .aspx page shows how you can write HTML directly to the return page by using the <asp:Literal> control. The code takes user input of a color name, inserts it into the HTML sent back, and displays text in the color entered. The page uses HtmlEncode to ensure the inserted text is safe.

		 <%@ Page Language="C#" AutoEventWireup="true"%>
	

		 <html>
		   <form id="form1" runat="server">
		     <div>
		       Color:&nbsp;<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox><br />
		       <asp:Button ID="Button1" runat="server" Text="Show color" 
		          OnClick="Button1_Click" /><br />
		       <asp:Literal ID="Literal1" runat="server"></asp:Literal>
		     </div>
		   </form>
		 </html>
	

		 <script runat="server">
		   private void Page_Load(Object Src, [EventArgs] e)
		   {
		     protected void Button1_Click(object sender, [EventArgs] e)
		     {
		       Literal1.Text = @"<span style=""color:" 
		         + [Server.HtmlEncode(TextBox1.Text)]
		         + @""">Color example</span>";
		     }           
		   }
		 </Script>
	

Potentially Dangerous HTML Tags

While not an exhaustive list, the following commonly used HTML tags could allow a malicious user to inject script code:
* <applet>
* <body>
* <embed>
* <frame>
* <script>
* <frameset>
* <html>
* <iframe>
* <img>
* <style>
* <layer>
* <link>
* <ilayer>
* <meta>
* <object>

An attacker can use HTML attributes such as src, lowsrc, style, and href in conjunction with the tags above to inject cross-site scripting. For example, the src attribute of the <img> tag can be a source of injection, as shown in the following examples.

		 <IMG SRC="javascript:alert('hello');">
		 <IMG SRC="java&amp;#010;script:alert('hello');">
		 <IMG SRC="java&amp;#X0A;script:alert('hello');">
	

An attacker can also use the <style> tag to inject a script by changing the MIME type as shown here:
		 <style TYPE="text/javascript">
		   alert('hello');
		 </style>
	

Step 5: Evaluate Countermeasures

When you find ASP.NET code that generates HTML using some input, you need to evaluate appropriate countermeasures for your specific application. Countermeasures include:
* Encode HTML output
* Encode URL output
* Filter user input

Encode HTML Output

If you write text output to a Web page and you do not know with absolute certainty that the text does not contain HTML special characters (such as <, >, and &), then pre-process it using the HttpUtility.HtmlEncode method. Do this if the text came from user input, or even a database, or a local file.

		 Response.Write(HttpUtility.HtmlEncode(Request.Form["name"]));
	

Do not treat encoding output as a replacement for checking that input is well-formed and correct. Use it as an additional security precaution.

Encode URL Output

If you return URL strings to the client that contain input, use HttpUtility.UrlEncode to encode URL strings as shown below.

		 [Response.Write(HttpUtility.UrlEncode(urlString));]
	

Filter User Input

If you have pages that need to accept a range of HTML elements, for example through some kind of rich text input field, ASP.NET request validation must be disabled for the page. If you have pages that do this, create a filter that allows only the HTML elements that you want to accept. A common practice is to restrict formatting to "safe" HTML elements such as bold <b> and italic <i>.
To safely allow restricted HTML input:
  1. Disable ASP.NET request validation by the adding the ValidateRequest="false" attribute to the @ Page directive.
  2. Encode the string input with the HtmlEncode method.
  3. 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 then 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 <b> and <i>
		     sb.Replace("&amp;lt;b&amp;gt;", "<b>");
		     sb.Replace("&amp;lt;/b&amp;gt;", "</b>");
		     sb.Replace("&amp;lt;i&amp;gt;", "<i>");
		     sb.Replace("&amp;lt;/i&amp;gt;", "</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>
	

Additional Considerations

In addition to the techniques discussed earlier, use the following countermeasures as added safe guards to prevent cross site scripting:
* Set the correct character encoding
* Do not rely on input sanitization
* Use the ""HttpOnly"" cookie option
* Use the <frame> security attribute
* Use the innerText property instead of innerHTML

Set the Correct Character Encoding

To successfully restrict valid data for your Web pages, you should limit the ways in which the input data can be represented. This prevents malicious users from using canonicalization and multi-byte escape sequences to trick your input validation routines. A multi-byte escape sequence attack is a subtle manipulation that uses the fact that character encodings, such as uniform translation format-8 (UTF-8), use multi-byte sequences to represent non-ASCII characters. Some byte sequences are not legitimate UTF-8, but may be accepted by some UTF-8 decoders, thus providing an exploitable security hole.
ASP.NET allows you to specify the character set at the page level or at the application level by using the <globalization> element in the Web.config file. The following code examples show both approaches and use the ISO-8859-1 character encoding, which is the default in early versions of HTML and HTTP.
To set the character encoding at the page level, use the <meta> element or the ResponseEncoding page-level attribute, as follows:

		 <meta http-equiv="Content Type" 
		       content="text/html; charset=ISO-8859-1" />
	

OR

		 <% @ Page ResponseEncoding="iso-8859-1" %>
	

To set the character encoding in the Web.config file, use the following configuration:

		 <configuration>
		    <system.web>
		       <globalization 
		          requestEncoding="iso-8859-1"
		          responseEncoding="iso-8859-1"/>
		    </system.web>
		 </configuration>
	

Validating Unicode Characters

Use the following code to validate Unicode characters in a page:

		 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.Form["name"],
		                      @"^[\p{L}\p{Zs}\p{Lu}\p{Ll}]{1,40}$"))
		     throw new ArgumentException("Invalid name parameter");
	

		   // Use individual regular expressions to validate other parameters
		   . . .
		 }
	

The following explains the regular expression shown in the preceding code:
* {name} specifies a named Unicode character class.
* \p{name} matches any character in the named character class specified by {name}.
* {L} performs a left-to-right match.
* {Lu} performs a match of uppercase.
* {Ll} performs a match of lowercase.
* {Zs} matches separator and space.
* {1,40} means no less that 1 and no more than 40 characters.
* $ means stop looking at this position.

Do Not Rely on Input Sanitization

A common practice is for code to attempt to sanitize input by filtering out certain known risky characters. Do not rely upon this approach because malicious users can generally find an alternative representation to bypass your validation. Instead, your code should validate for known secure, safe input. Table 1 shows various ways to represent some common characters.
Table 1: Character representation
CharactersDecimalHexadecimalHTML Character SetUnicode
" (double quotes)&#34&#x22&quot;\u0022
' (single quotes)&#39&#x27&apos;\u0027
& (ampersand)&#38&#x26&amp;\u0026
< (less than)&#60&#x3C&lt;\u003c
> (greater than)&#62&#x3E&gt;\u003e

Use the ""HttpOnly"" Cookie Option

Internet Explorer 6 Service Pack 1 supports an HttpOnly cookie attribute, which prevents client-side script from accessing the cookie from the document.cookie property. Instead, an empty string is returned. The cookie is still sent to the server whenever the user browses to a Web site in the current domain.

Note: Web browsers that do not support the HttpOnly cookie attribute either ignore the cookie or ignore the attribute, which means it is still subject to cross site scripting attacks..

The System.Net.Cookie class in the .NET Framework 2.0 supports an HttpOnly property. You can set this property programmatically, or by using the new <httpCookies> element in your application's Web.config. By default, the HttpOnly cookie property is not set. You can see this by examining the following default setting from Machine.config.comments.

		 <httpCookies httpOnlyCookies="false" requireSSL="false" domain="" />
	

To set the HttpOnly property of the cookie, add the following entry in your application's Web.config.

		 <system.web>
		  ...
		   <httpCookies httpOnlyCookies="true" requireSSL="false" domain="" />
		  ...
		 </system.web>
	

On earlier versions of the .NET Framework (1.0 and 1.1), you need to add code similar to the following to your application's Application_EndRequest event handler in Global.asax to explicitly set the HttpOnly attribute:

		 protected void Application_EndRequest(Object sender, [EventArgs] e) 
		 {
		   string authCookie = [FormsAuthentication.FormsCookieName;]
		   foreach (string sCookie in Response.Cookies) 
		   {
		     // Just set the [HttpOnly] attribute on the Forms 
		     // authentication cookie. Skip this check to set the attribute 
		     // on all cookies in the collection
	

		     if (sCookie.Equals(authCookie))
		     { 
		       // Force [HttpOnly] to be added to the cookie header
		       Response.Cookies[sCookie].Path += ";HttpOnly";
		     }
		   }
		 }
	

Use the <frame> Security Attribute

Internet Explorer 6 and later supports a new security attribute on the <frame> and <iframe> elements. You can use the security attribute to apply the user's Restricted Sites Internet Explorer security zone settings to an individual frame or iframe. By default, the Restricted Sites zone does not support script execution.
If you use the security attribute, it must currently be set to "restricted" as shown below:

		 <frame security="restricted" src="http://www.somesite.com/somepage.htm"></frame>
	

Use The innerText Property Instead of innerHTML

If you use the innerHTML property to build a page and the HTML is based on potentially untrusted input, you must use HtmlEncode to make it safe. To avoid having to remember to do this, use innerText instead. The innerText property renders content safe and ensures that script is not executed.
The following example shows this approach for two HTML <span> controls. The code in the Page_Load method sets the text displayed in the Welcome1 <span> element using the innerText property, so HTML encoding is unnecessary. The code sets the text in the Welcome2 <span> element by using the innerHtml property; therefore, you must HtmlEncode it first to make it safe.
		 <%@ Page Language="C#" AutoEventWireup="true"%>
	

		 <html>
		   <body>
		     <span id="Welcome1" runat="server"> </span>
		     <span id="Welcome2" runat="server"> </span>
		   </body>
		 </html>
	

		 <script runat="server">
		   private void Page_Load(Object Src, [EventArgs] e)
		   {
		     // Using [InnerText] renders the content safe – no need to [HtmlEncode]
		     [Welcome1.InnerText] = "Hello, " + User.Identity.Name;
	

		     // Using [InnerHtml] requires the use of [HtmlEncode] to make it safe
		     [Welcome2.InnerHtml] = "Hello, " + 
		                         [Server.HtmlEncode(User.Identity.Name);]
		   }
		 </Script>
	

Summary

Cross-site scripting attacks exploit vulnerabilities in Web page validation by injecting client-side script code. Common vulnerabilities that make your Web applications susceptible to cross-site scripting attacks include failing to properly validate input, failing to encode output, and trusting the data retrieved from a shared database.
To protect your application against cross-site scripting attacks, assume that all input is malicious. Constrain and validate all input. Encode all output that could, potentially, include HTML characters. This includes data read from files and databases.

Additional Resources

How To: Use Regular Expressions to Constrain Input in ASP.NET 2.0.
How To: Protect From SQL Injection in ASP.NET 2.0.
How To: Protect From Injection Attacks in ASP.NET 2.0
How To: Use Code Access Security with ASP.NET 2.0



Return to HomePage
Microsoft Communities