Defenses against XSS

In this lesson, we will discuss​ defenses against cross-site scripting.

HTML encoding as defense

Now that we see how HTML encoding works, we can see how we can use this as a defense against HTML injection and XSS. Whenever we’re building up HTML as part of our response to a web browser, if we ever concatenate in user-controlled data, we need to HTML-encode it first. That way, even if an attacker tries to sneak JavaScript into one of our responses, we’ll encode it first and the browser will just display JavaScript source code to the user instead of executing attacker-controlled JavaScript.

The preferred defense is to use the encoding libraries that come with your web framework. That is, most web frameworks have built-in libraries that will HTML-encode user-supplied data like this:

<script>alert('Ha Ha!');</script>

into this:

&lt;script&gt;alert('Ha Ha!');&lt;/script&gt;

or this:

&#x3C;script&#x3E;alert(&#x27;Ha Ha!&#x27;);&#x3C;/script&#x3E;

As with the previous example, the solution here is to use our web framework’s HTML encoding library. Proper encoding would result in markup like this:

<img src="picture.jpg" alt="blah&#x22; onload=&#x22;$(&#x27;#submitbutton&#x27;).click();" />

The quotes are replaced by &#x22; so the onload is just part of the alt text instead of a new attribute. The HTML encoding prevents the attack.

Handling attacker-controlled data in other contexts

Sometimes XSS payloads don’t look much like textbook XSS payloads if they’re built on top of JavaScript frameworks like AngularJS. For more details on Angular-specific attacks, see the excellent article “XSS without HTML: Client-Side Template Injection with AngularJS” by Gareth Heyes.

XSS by way of AngularJS expression injection doesn’t need < or >, so traditional web framework escaping doesn’t help. In general, you shouldn’t need to allow dynamic content inside of a dom element that’s decorated with the ng-app attribute. But if for some strange reason you do, be sure to encode the {{ and }} so that attackers can’t inject an AngularJS expression.

In summary, the way to prevent XSS is to restrict user-controlled data in as few kinds of places as possible in a web page. Keep user-controlled input out of dom elements decorated with the ng-app attribute that marks the start of an Angular JS application. And keep user-controlled data out of JavaScript. If you can do this and keep user-controlled data between HTML tags, then you can definitely prevent XSS by making sure to HTML-encode all user-controlled data.

If you really can’t get away without including dynamic data in other kinds of places in your markup (such as inside JavaScript,) consult the OWASP XSS prevention cheat sheet. There are a lot of surprising gotchas to allowing dynamic data throughout your markup.

In the next lesson, we’ll get an introduction to another vulnerability, cross-site request forgery.

Get hands-on with 1200+ tech skills courses.