XSS Remedies

XSS Remedies

As with all user input issues, the first rule for mitigating XSS issues is to determine which input is valid and to reject all other input. (Have I said that enough times?) I'm not going to spend much time on this because this topic has been discussed ad nauseam in the previous three chapters. That said, not trusting the input is the only safe approach. Fixing XSS issues is a little like fixing SQL injection attacks you have a hugely complex grammar to deal with, and certain characters have special meaning.

Other defense in depth mechanisms do exist, and I'll discuss some of these, including the following:

  • Encoding output

  • Adding double quotes around all tag properties

  • Inserting data in the innerText property

  • Forcing the codepage

  • The Internet Explorer 6.0 SP1 HttpOnly cookie option

  • Internet Explorer Mark of the Web

  • Internet Explorer <FRAME SECURITY> attribute

  • ASP.NET 1.1 ValidateRequest configuration option

You should think of all these items except the first as defense in depth strategies because, frankly, there is only one way to solve the issue, and that's for the server application to be hard-core about what constitutes valid input. Let's look at each of these.

Encoding Output

Encoding the input data before displaying it is a good practice. Luckily, this is simple to achieve using the ASP Server.HTMLEncode method or the ASP.NET HttpServerUtility.HTMLEncode method. These methods will convert dangerous symbols, including HTML tags, to their harmless HTML representation for example, < becomes &lt;.

Adding Double Quotes Around All Tag Properties

Sometimes the attacker's data becomes part of an HTML tag; in fact, it's very common. For example, www.contoso.com/product.asp?id=210502 executes this ASP code:

<a href=http://www.contoso.com/detail.asp?id=<%= request.querystring("id") %>>

which yields the following HTML:

<a href=http://www.contoso.com/detail.asp?id=2105>

Exploiting this requires that the attacker provide an id value that closes the <a> tag and creates a <script> tag, This is very easy simply make id equal to 2105><script event=onload>exploitcode</script>.

In some cases, the attacker need not close the <a> tag; he can extend the properties of the tag. For example, 2105 onclick="exploitcode" would extend the <a> tag to include an onclick event, and when the user clicks the link, the exploit code executes.

The Web developer can defend against this attack by placing optional double quotes around each tag attribute, like so:

<a href="http://www.contoso.com/detail.asp?id=<%= Server.HTMLEncode (request.querystring("id")) %>">

Note the double quotes around the href reference. It doesn't matter if the attacker provides a malformed id value, because detail.asp will treat the entire input not simply the first value that constitutes a valid id as the id. For example, 2105 onclick='exploitcode' becomes this:

<a href="http://www.contoso.com/detail.asp?2105 onclick='exploitcode'">

I doubt 2105 onclick='exploitcode' is a valid product at Contoso.

So why not use single quotes rather than double quotes? The reason is HTML encoding doesn't escape single quote characters, but it does escape double quotes.

Inserting Data in the innerText Property

The innerText property renders arbitrary content inert and is safe to use when building content based on any user input. The following shows a simple example:

<html> <body> <span id=spnTest></span> </body> </html> <script for=window event=onload> spnTest.innerText = location.hash; </script>

If you invoke this HTML code with the following URL, you'll notice the script is rendered inert.

file://C:\webfiles/xss.html#<script>alert(1);</script>

The innerHTML property is actively discouraged when populating a page with untrusted input. I'm sure you can work out why!

Forcing the Codepage

If your Web application restricts what is valid in a client request, it should also limit other representations of those characters. Setting a codepage, such as by using the following <meta> tag, in your Web pages will protect against the attacker using canonicalization tricks that could represent special characters using multibyte escapes:

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

This character set contains all characters necessary to type Western European languages. This encoding is also the most common encoding used on the Internet. It supports the following languages: Afrikaans, Catalan, Danish, Dutch, English, Faeroese, Finnish, French, German, Galician, Irish, Icelandic, Italian, Norwegian, Portuguese, Spanish, and Swedish. For completeness, ISO-8859 supports the following languages:

  • 8859-2 Eastern Europe

  • 8859-3 South Eastern Europe

  • 8859-4 Scandinavia (mostly covered by 8859-1)

  • 8859-5 Cyrillic

  • 8859-6 Arabic

  • 8859-7 Greek

  • 8859-8 Hebrew

The Internet Explorer 6.0 SP1 HttpOnly Cookie Option

During the Windows Security Push, the Internet Explorer security team devised a way to protect the browser from XSS attacks that read the client's cookie from script. The remedy is to add an HttpOnly option to the cookie. For example, the following cookie cannot be accessed by DHTML in Internet Explorer 6.0 SP1:

Set-Cookie: name=Michael; domain=Microsoft.com; HttpOnly

The browser will simply return an empty string if the insecure script code originating from the server attempts to read the document.cookie property. You can use the following ISAPI filter code, available in the download code, if you want to enforce this option for all cookies used by your Internet Information Services (IIS) based Web servers.

// Portion of HttpOnly ISAPI filter code DWORD WINAPI HttpFilterProc( PHTTP_FILTER_CONTEXT pfc, DWORD dwNotificationType, LPVOID pvNotification) { // Hard code cookie length to 2k CHAR szCookie[2048]; DWORD cbCookieOriginal = sizeof(szCookie) / sizeof(szCookie[0]); DWORD cbCookie = cbCookieOriginal; HTTP_FILTER_SEND_RESPONSE *pResponse = (HTTP_FILTER_SEND_RESPONSE*)pvNotification; CHAR *szHeader = "Set-Cookie:"; CHAR *szHttpOnly = "; HttpOnly"; if (pResponse->GetHeader(pfc,szHeader,szCookie,&cbCookie)) { if (SUCCEEDED(StringCchCat(szCookie, cbCookieOriginal, szHttpOnly))) { if (!pResponse->SetHeader(pfc, szHeader, szCookie)) { // Fail securely - send no cookie! pResponse->SetHeader(pfc,szHeader,""); } } else { pResponse->SetHeader(pfc,szHeader,""); } } return SF_STATUS_REQ_NEXT_NOTIFICATION; }

You can perform a similar task in ASP.NET:

HttpCookie cookie = new HttpCookie("Name", "Michael"); cookie.Path = "/; HttpOnly"; Response.Cookies.Add(cookie);

This will set the HttpOnly option to a single cookie in the application; you can make the setting application-global by hooking the Application_OnPreSendRequestHeaders method in global.asax.

Likewise, you can use code like this in an ASP page:

response.addheader("Set-Cookie","Name=Mike; path=/; HttpOnly; Expires=" + CStr(Now))

CAUTION
Although HttpOnly is a good defense in depth mechanism, it does not defend against cookie-poisoning attacks; it only prevents malicious script from reading the cookie. Enabling this option in your cookies is not a complete solution.

Internet Explorer Mark of the Web

Earlier I mentioned the problem of XSS issues in local HTML files. Internet Explorer allows you to force HTML files into a zone other than the My Computer zone. This feature, available since Internet Explorer 4.0, is often referred to as the mark of the Web, and you may have noticed it if you saved a Web page from the Internet onto your desktop. Look at Figure 13-2. This was captured from msdn.microsoft.com and saved locally, yet the zone is not My Computer it's in the Internet zone, because that's where the HTML pages came from.

figure 13-2 the msdn homepage saved locally, yet it's in the internet zone, not the my computer zone.

Figure 13-2. The MSDN homepage saved locally, yet it's in the Internet zone, not the My Computer zone.

The secret to this is a comment placed in the file:

<!-- saved from url=(0026)http://msdn.microsoft.com/ -->

When Internet Explorer loads this file, it looks for a saved from url comment, and then it reads the URL and uses the zone settings on the computer to determine what security policy to apply to the Web page. If your policy prohibits certain functionality in the Internet zone (scripting, for example) but allows it in the My Computer zone, this Web page cannot use script because it has been forced into the Internet zone. The (0026) value is the string length of the URL.

You should set such a comment in your Web pages linking back to your Web site. That way the more restrictive policy is always enforced, regardless of how the Web page is accessed. This also applies to local HTML content setting this option can force local HTML files into a more secure zone.

Internet Explorer <FRAME SECURITY> Attribute

Internet Explorer 6 and later introduced a new <FRAME> attribute to prohibit dangerous content in pages loaded into frames. The SECURITY attribute applies the user's zone settings to the source file of a frame or iframe. The following example outlines how to use this property:

<IFRAME SECURITY="restricted" src="/books/1/287/1/html/2/http://www.contoso.com"></IFRAME>

This will force the Web site into the Restricted Sites zone, where by-default script cannot execute. Actually, not a great deal of functionality is available to a Web site in the Restricted Sites zone! If a frame is restricted by the SECURITY attribute, all nested frames share the same restrictions.

You should consider wrapping your Web site pages in a frame and using this attribute if there are ways to work around other defensive mechanisms. Obviously, this only protects uses of Internet Explorer, and not other browsers.

More Info
Presently, the only valid <FRAME SECURITY> setting is restricted'.

ASP.NET 1.1 ValidateRequest configuration option

Before I explain this new ASP.NET 1.1 capability, you should realize that this does not solve the XSS problem; rather it helps reduce the chance that you accidentally leave an XSS defect in your ASP.NET code. Nothing more! By default, this option is enabled, and you should leave it that way until you are happy you have fixed all potential XSS vulnerabilities in your code. Even then I'd leave it turned on as a small insurance policy!

By default, this feature will check that users are not attempting to set HTML or script in cookies (HttpRequest.Cookies), query strings (HttpRequest.QueryString) and HTML forms (HttpRequest.Form.) If the request contains this potentially dangerous input an HttpRequestValidationException exception is thrown.

You can set the option as a page directive:

<%@ ValidateRequest="False" %>

or in a configuration file:

<!-- configuration snippet: can be in machine.config or a web.config can be scoped to an individual page using <location> around the <system.web> element --> <configuration> <system.web> <pages validateRequest="true"/> </system.web> </configuration> 

Remember, the default is true and all requests are validated, so you must actively disable this feature.



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2001
Pages: 286

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net