A LIST Apart: For People Who Make Websites

No. 215
April 18, 2006

Community Creators, Secure Your Code!

Community Creators, Secure Your Code!

Personalization is a great feature—it allows users to make their personal pages come to life by adding colors, pictures, and even sound—but as with any user input, it is a security threat if not properly sanitized. The creation of a secure online community is a balancing act: your users should be able to personalize their pages using pseudo code or actual HTML, while remaining protected from vandals who might inject malicious JavaScript or otherwise cause harm.

One piece of the larger security puzzle is cross-site scripting (XSS). In part one of this two-article series, we will look at various XSS techniques you should be aware of, and at common methods of defending your community against them. In part two, we’ll use real-world examples to explore these techniques in greater detail.

The threat

Malicious JavaScript injections are a threat at many levels. Using a full-fledged injection, an attacker could:

  • Change the presentation of the attacker’s personal pages in a forbidden way (this is the lowest level of severity, but could produce a misleading or confusing experience for other users).
  • Execute an action whenever a user enters the attacker’s page, such as voting for the attacker in a poll or adding the attacker to a buddy or “trusted” list.
  • Infect the personal pages of users who visit the attacker’s page, creating a spreading virus that might, in turn, execute malicious code or propagate spyware /viruses that exploit security flaws in popular browsers.

These are just three examples of what an attacker might do, but two things are already clear:

  1. XSS is a real threat. MySpace and many other community sites have already been attacked or compromised.
  2. Webmasters should, therefore, make sure that their sites are properly protected.

A real-world example using eval() and AJAX

By using the eval() function, an attacker can execute long JavaScript commands and even self-made functions. The attacker could, for instance, use the XMLHTTP request object (the core component of AJAX) to send or retrieve a piece of information. A insertion that would force the victim to vote for the attacker could look something like this:

eval(
  // IE only (to shorten the example)
  http_request = new ActiveXObject("Microsoft.XMLHTTP");

  // The string to POST (Taken from the community)
  send = "vote-id=123456789&vote=10";

  // We send the data to our function “nullfunction” 
  http_request.onreadystatechange = nullfunction;

  // Send it to the right page
  http_request.open("POST", "voteOnAuser.php", true);
  // Sending as form data
  http_request.setRequestHeader("Content-type", "application/
   x-www-form-urlencoded");
  http_request.setRequestHeader("Content-length", send.length);
  http_request.setRequestHeader("Connection", "close");

  // Send
  http_request.send(send);

  // In our case we don’t want to use the data being returned. 
    If we’d wanted to (for example, to get our user id or any 
    other information) we could have used this function to 
    process the data. 
  function nullfunction() {
  if (http_request.readyState  4 && http_request.status  200) {}
  }
}

Of course this is just an example, but if the attacker injected such a script into his personal page, he could do a lot of damage.

IE, CSS, and JavaScript

Thanks to IE’s predilection for executing JavaScript, many communities are left vulnerable. IE will accept and execute the following code:

[…] style="background:url(javascript:alert(document.cookie))” […]

It’s bad if a browser executes JavaScript from style tags, because many communities don’t validate this input—they simply take the input, strip single and double quotes, and print it out. This, for example:

[color=black; background:url(javascript:alert(document.cookie));]
 [/color]

Would be translated into this:

<font style=”color: black; background:url(javascript:alert
  (document.cookie));”></font>

By blocking the word “JavaScript,” many of us feel safe, but we are still vulnerable since the following example is perfectly valid as far as Internet Explorer is concerned:

<font style="background:url(jav
ascr
ipt:alert(document.cookie))"></font>

If I were to inject this into a community that blocks “JavaScript,” I would simply use:

[color=black; background:url(jav
ascr
ipt:alert(document.cookie));][/color]

Protecting yourself

Sanitizing user input may sound easy, but this may not always be true. Several factors conspire to complicate the process:

  • HTML contains many elements that have both legitimate and non-legitimate uses.
  • Browser inconsistencies let users execute JavaScript hidden in CSS, as seen above.
  • Self-created tags such as [URL] present new opportunities for abuse.

The surest way to protect your site is to convert all applicable characters to their HTML entities, but this is not really an option if you want to allow user customization.

Protecting IE from itself

So how does one help IE protect itself? The answer depends on the needs of your users. The first thing you should do is think about what your users should be allowed to do, and narrow it down if you can. Do they need all the possible style properties? Maybe color, font-size, and a handful of others are enough for your visitors.

Once you’ve narrowed down your allowed personalizations, a white list is an efficient way to manage input: just lock down input for allowed tags and properties, including styles. If the user is allowed to change font color, there is no need for it to be anything but three to six characters ranging from A to Z and 0 through 9. If you recive anything else, simply reject it.

Using this method, you retain total control of what is and isn’t allowed. If you want your users to be able to use all CSS codes and don’t feel like making regular expressions for every syntax, then at least make sure you properly replace the word “JavaScript.” (Using regular expressions, look for line breaks as described in the above section, “IE, CSS, and JavaScript”.)

Checklist for validating input

While most community managers already know these basics, here’s a short list for newcomers:

  • Strip out single and double quotes or convert them to their HTML entities (&#8216; and &#8217; for opening and closing single quotes, &#8220; and &#8221; for opening and closing double quotes). Please note however, that this does not entirely protect you. An attacker could still use String.fromCharCode(39) in an eval() function.
  • Convert < and > to &#60; and &#62;.
  • Convert all line breaks to <br>. If you do this on all code, including style tags, you will save yourself from an attack. See “IE, CSS and JavaScript”.
  • Check your self-created code tags (such as [URL]) to make sure the user is not allowed to inject JavaScript in URLs or CSS.
  • Consider stripping out the word “script” to prevent someone from trying to inject the word JavaScript. Keep in mind, though, that as far as IE is concerned, “ja\n\sc\nript” is valid.
  • Use regular expressions (server side) to validate and sanitize user input, as described above
  • Validate CSS input!

In conclusion

User customization is great for users, but can be a nightmare for community creators and other coders who actively deal with user input. Validating and sanitizing user input is no longer optional. Consider what your users really need to do, think about what characters they need to accomplish those tasks, and strip/convert as necessary to protect your community.

In part two, we’ll examine specific injections and techniques in detail and get our hands dirty with some actual code.

Learn More

Related Topics: Community, Scripting

Discuss

Was it good for you, too? Join the discussion »

About the Author

Niklas Bivald Niklas Bivald is a Swedish student who spends his time studying, making websites and e-commerce systems and keeping a bunch of projects up and running. Yes, Sweden is in Europe, and no, we don’t have polar bears.