XSS Prevention in Four Simple Steps

Preventing Cross Site Scripting (XSS) attacks is a daunting task for developers. In short, XSS attacks are an injection attack in which data that is structurally significant in the current context changes the intended semantics and/or functionality. While there are great resources online that walk you through prevention techniques (one of the best security resources is The Open Web Application Security Project, or OWASP, website), it’s easy to get confused when you try to implement all of the necessary safeguards.

Below, I’ve outlined four simple steps that significantly lower the risk of XSS attacks against your website. By being a bit more restrictive, we can simplify our approach to preventing XSS in the most common use cases. These steps must all be implemented together, but there’s only four of them, so c’mon, you can do it 🙂

Step 1: Escape Output Provided by Users

If you want to include data within a page that’s been provided by users, escape the output. And, in this simplified list, we’re going to stick with one simple escape operation: HTML encode any <>&. For example, PHP provides the htmlspecialchars() function to accomplish this common task.

Step 2: Always Use XHTML

Read through OWASP’s XSS prevention strategies, and it becomes apparent that protecting against injection requires much more effort if you use unquoted attributes in your HTML. In contrast, in quoted attributes, escaping data becomes the same process needed to escape data for content within tags, the escape operation we already outlined above. That’s because the only troublemaker in terms of sneaking in structurally significant content within the context of a quoted attribute is the closing quote.

Obviously, your markup doesn’t have to be XHTML in order to contain quoted attributes. However, shooting for and validating against XHTML makes it easy to test if all of the attributes are quoted.

Step 3: Only Allow Alphanumeric Data Values in CSS and JavaScript

We need to limit the data you allow from users that will be output within CSS and Javascript sections of the page to alphanumeric (e.g., a regex like [a-zA-Z0-9]+) types, and make sure they are used in a context in which they truly represent values. In Javascript this means user data should only be output within quoted strings assigned to variables (e.g., var userId = “ALPHANUMERIC_USER_ID_HERE”;.) In CSS this means that user data should only be output within the context for a property value (e.g., p { color: #ALPHANUMERIC_USER_COLOR_HERE;}.) This might seem Draconian, but, hey, this is supposed to be a simple XSS tutorial 😉

Now, to be clear, you should always validate user data to make sure it meets your expectations, even for data that’s output within tags or attributes, as in the earlier examples. However, it’s especially important for CSS and JavaScript regions, as the complexity of the possible data structures makes it exceedingly difficult to prevent XSS attacks.

Common data you might want users to be able supply to your JavaScript such as Facebook, Youtube, and Twitter ID’s can all be used whilst accommodating this restriction.   And, CSS color attributes and other styles can be integrated, too.

Step 4: URL-Encode URL Query String Parameters

If user data is output within a URL parameter of a link query string, make sure to URL-encode the data.  Again, using PHP as example, you can simply use the urlencode() function. Now, let’s be clear on this and work through a couple examples, as I’ve seen much confusion concerning this particular point.

Must URL-encode

The following example outputs user data that must be URL-encoded because it is used as a value in the query string.

<a href=”http://site.com?id=USER_DATA_HERE_MUST_BE_URL_ENCODED”>

Must Not URL-Encode

The following example outputs the user-supplied data for the entire URL. In this case, the user data should be escaped with the standard escape function (HTML encode any <>&), not URL-encoded. URL-encoding this example would lead to malformed links.

<a href=”USER_DATA_HERE_MUST_USE_STANDARD_HTML_ESCAPING”>

Summary

Let me be clear: These four steps don’t instantly secure a website against all XSS attacks, and I purposely skipped over some very important, related topics (cookie settings, DOM Injection, the risk of including user-provided data in data-schemed URI’s, other contexts such as SVG, etc) that the OWASP website covers very well. Additionally, the limitations outlined above may be too restrictive for some websites, although the number of websites that truly has to offer more flexibility in terms of CSS and Javascript output is likely very small.

That said, these four steps provide a an approach to defending against XSS that is easily remembered and implemented, covers a broad range of typical website scenarios, and serves as a solid start for developers who are learning how to address basic security concerns.

One thought on “XSS Prevention in Four Simple Steps”

Leave a Reply

Your email address will not be published. Required fields are marked *