Stop Cross-site Scripting Attacks in their Tracks

Cross-site scripting attacks can steal your identity and compromise your Web server. A few simple steps can safeguard your software.

Ask the Pro

Languages: JavaScript | C#



Stop Cross-site Scripting Attacks in their Tracks

Cross-site scripting attacks can steal your identity and compromise your Web server. A few simple steps can safeguard your software.


By Jeff Prosise


Q: Are ASP.NET applications vulnerable to cross-site scripting attacks? If so, what can you do to prevent them?


A: You can't prevent cross-site scripting attacks from occurring, but you can make sure they're not successful. All it takes is knowledge of how cross-site scripting attacks (also known as XSS attacks) work and adherence to a few simple rules as you write your code. ASP.NET 1.0 doesn't shield you from such attacks automatically. ASP.NET 1.1 does by virtue of a new feature referred to as request validation. I'll elaborate on this important addition to the framework later in the article.


Cross-site scripting is a technique hackers use to execute client-side script - possibly malicious client-side script - in someone else's browser. The script usually comes from user input that is echoed to a page in raw, unfiltered form. Consider this ASP.NET page:




      Please enter your name:



        RunAt="server" />






This page invites the user to type in a name. Then, the page uses that name to display a personalized greeting. Unfortunately, this page is wide open to cross-site scripting attacks in ASP.NET 1.0. Why? Because it echoes the user's input to the page without validation or filtering. To demonstrate, run the page and type this into the textbox:



When you click on the Submit button, the message box in Figure 1 appears.


Figure 1. A rudimentary scripting attack results in the display of this message box.


What happened? Check out the HTML returned to the browser following the postback:





      Please enter your name:









The problem lies in the boldfaced text, which the browser sees as an innocent block of client-side script to execute when the page loads. The fact that the application naively echoes the user's input to the page enables that user to inject client-side script into the page returned to him or her.


Although using the JavaScript "alert" command to pop up a message box in your own browser seems rather harmless, cross-site scripting attacks can be much more devious. Suppose an attacker includes a block of client-side script in a message posted in a discussion forum. Furthermore, suppose the forum doesn't filter user input and the script posted there does something sinister - such as redirect the user to another Web site. An unsuspecting user who begins browsing the message threads suddenly could find himself or herself surfing a notorious pornography site. That's the dark side of cross-site scripting. And it gets worse.


Check out the .aspx file in Figure 2. It models a simple search site that accepts input from a textbox or query string. To demonstrate, run it on a server outfitted with ASP.NET 1.0, enter some search text, and click on the Search button. You'll be told your search yielded no results (see Figure 3). For good measure (and for demonstration purposes), this page issues each visitor a cookie containing a credit-card number.














Search for:




              RunAt="server" />







Figure 2. DumbSearch.aspx is vulnerable to cross-site scripting attacks because it echoes raw user input to the page.


Figure 3. This is DumbSearch.aspx in action.


Because it echoes search strings to the page without filtration, this page is vulnerable to cross-site scripting attacks. An attacker can leverage this vulnerability to steal the credit-card numbers encoded in the search site's cookies.


The attack works like this. In an e-mail message, the attacker sends the HTML in Figure 4 to an unsuspecting victim who happens to be a user of the search site. The victim clicks on the "Yes, I want to claim my one million dollars!" link, which takes the victim to the search site (DumbSearch.aspx) and programmatically redirects him or her to the attacker's site (EvilPage.aspx). Redirection is accomplished by a snippet of client-side script embedded in the query string passed to DumbSearch.aspx - a snippet that DumbSearch.aspx obligingly echoes to the page. Worse yet, the script transmits the cookie containing the user's credit-card number to the hacker's site in the form of a query string. This cookie is available because the script executes with the browser pointing to DumbSearch.aspx.



    Congratulations! You have won $1,000,000!

    Click the link below for information on how to

    obtain your prize.

    Yes, I want to claim my one million dollars!


Figure 4. Jackpot.html includes a malicious link that, when clicked on, steals the cookie issued by DumbSearch.aspx and transmits it to a hacker's site.


To witness the attack first-hand, first copy DumbSearch.aspx, EvilPage.aspx, and Jackpot.html to wwwroot. Next, open DumbSearch.aspx in your browser and perform a search or two. Finally, open Jackpot.html in your browser and click on "Yes, I want to claim my one million dollars!" See Figure 5 for the result.


Figure 5. Gotcha! This is proof that clicking on the link in Jackpot.html enables an attacker to swipe your cookie.


In real life, EvilPage.aspx might provide bogus instructions for claiming the money. The demo version proves it stole your credit-card number by displaying it on the screen. Figure 6 shows EvilPage.aspx's source code. Reading the credit-card number is as simple as reading the query string.


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




      if (Request["Cookie"] != null) {



Congratulations, sucker!


          Response.Write (Request["Cookie"]);




Figure 6. EvilPage.aspx displays the stolen cookie in a browser window. Real hackers won't be so nice.


This example is hardly contrived. In fact, stealing cookies is one of the most common uses for cross-site scripting attacks. Imagine the damage a hacker could do if he or she could steal cookies issued to you by eBay,, and other large commercial sites.


What can you do to code against cross-site scripting attacks? If you follow two simple rules, you'll find cross-site scripting is virtually powerless against you. First, if possible, never echo user input to a page without filtering it first. A username, for example, has no business containing angle brackets. Use ASP.NET validation controls to limit usernames to letters and numbers and you'll strike a blow against cross-site scripting. Second, when you must allow users to enter angle brackets and other characters used in cross-site scripting attacks, HTML-encode the input before echoing it to the page. In ASP.NET, you can use Server.HtmlEncode to do the encoding.


Figure 7 contains the source code for an improved version of DumbSearch.aspx that's resistant to cross-site scripting attacks. Try it: Replace DumbSearch.aspx in Jackpot.html with SmartSearch.aspx. Clicking on the link no longer redirects you to EvilPage.aspx because SmartSearch.aspx's SearchFor method HTML-encodes the search string before writing it to the page.














Search for:




              RunAt="server" />







Figure 7. SmartSearch.aspx foils cross-site scripting attacks by HTML-encoding input before echoing it to the page.


Cross-site scripting attacks can take other forms, too, such as tags that download hostile ActiveX controls to unwitting users' computers and tags that run Java applets. For more information regarding cross-site scripting and the hazards it poses, visit or go to Google and type "cross-site scripting."


ASP.NET 1.1 to the Rescue

Now for the good news: ASP.NET 1.1 includes built-in protection against cross-site scripting attacks. Running DumbSearch.aspx on an ASP.NET 1.1 server and clicking on the link in Jackpot.html produces the page shown in Figure 8. Why? Because version 1.1 scans input strings automatically for potentially injurious characters and character sequences, such as Note that, as an extra means of protection, it's still advisable to HTML-encode user input before outputting it to a page - doubly safe is doubly secure.


Figure 8. ASP.NET 1.1's request-validation feature throws an exception when it detects potentially harmful input.


By default, request validation in ASP.NET 1.1 is enabled. Normally, you want it enabled, but you might want to disable it temporarily for testing purposes (for example, to see DumbSearch.aspx succumb to attack on an ASP.NET 1.1 server) or disable it altogether to allow input containing