Donʼt cross the sites!

Dealing with Cross Site Scripting (XSS)

FrOSCon 2015 Frederic Hemberger

FrOSCon 2015 / Cologne.js

What is Cross Site Scripting?

- manipulate the siteʼs appearance
- manipulate cookies, localStorage, etc.
- steal session credentials, tokens, etc.
- load malicious code via XMLHttpRequest
- do whatever the heck you want

Type of XSS attacks

Reflected (non-persistent) XSS attacks:
Usually delivered by other websites or emails<script>alert(1)</script>

Reflected attacks on client side: DOM XSS

The text content of the search field is displayed unfiltered on the page.
Even <script>alert(1)</script>

Stored (persistent) XSS attacks:
Delivered to every visitor of the site

How do XSS vectors look like?

Just a very small range:

<​frameset onload=alert(1)>
<​body oninput=alert(1)><​input autofocus>
<​video poster=javascript:alert(1)//></video>
<!--<img src="--><​img src=x onerror=alert(1)//">
<​iframe src="data:text/html,&lt;iframe src='data:text/html,%26lt;iframe onload=alert(1)&gt;'&gt;"></iframe>
<​script>alert `1`</script>

Some browsers try to help you …

  • Google Chrome/Webkit: XSSAuditor
  • Internet Explorer 8+:
    HTTP-Header X-XSS-Protection: 1; mode=block
  • Firefox: Ticket #528661 is open since 2009 …

Secure input and output handling

What is user input?

  • The URL (including path or query parameters)
  • HTTP method (GET, POST, PUT, DELETE, …) and headers
  • Form values, file uploads
  • Cookies, browser storage
  • Data exchanged with APIs, Flash, applets, etc.

Validate user input

Data type validation
String, Number, Boolean, Date, undefined, …

Range and constraint validation
Negative/exponential numbers, String lengths,
"Your age is not 150 years", …

Business logic validation
"You must be at least 14 years old to register"

Sanitize user input

  • Remove ASCII control characters, null bytes, Unicode LTR/RTL control characters, Unicode whitespace, …
  • (Usually) remove all HTML

But what if you canʼt go without HTML?

Bad idea #1:
Filter out <script>, onload, onerror, etc.

Obviously not a good idea:


<img src=j&#x41vascript:alert('1')>

Bad idea #2:
»Everybody stand back:
I know Regular Expressions!«

HTML is an irregular language and Regular Expressions
are inadequate for the job.

Bad idea #3:
Filter out HTML in the front-end with JavaScript

As seen many times on StackOverflow™

                        // never, never, NEVER use this!
var el = document.createElement("DIV");
el.innerHTML = '<div>My HTML string</div>';
return el.textContent || el.innerText;

// never, never, NEVER use this! (part 2)
return $('<div/>').html('<div>My HTML string</div>').text();


Instead …

… use tools like DOM Purify (JS), HTML Purifier (PHP) or
AntiSamy (Java, .NET).

Encode output where necessary

Choose the correct encoding depending on the context

Testing and automation

»A test engineer walks into a bar.
Orders a beer.
Orders 999999999 beers.
Orders a lizard.
Orders -1 beers.
Orders a sfdeljknesv.«

Make security part of your build process

  • Check for outdated/vulnerable dependencies
  • Lint your code, aim for high code coverage in your unit tests
  • Check inputs (forms, APIs, etc.) during integration tests:
    Invalid input, null bytes, Unicode characters, HTML snippets, XSS vectors, etc.

Be aware of possible side channels.

Content Security Policy handles unwanted 3rd-party content.

Content Security Policy example

<!-- index.html -->
<!DOCTYPE html>
    <script src=""></script>
    <script src=""></script>

Content Security Policy example

# .htaccess
Header always set Content-Security-Policy: ↩
  "script-src 'self'"

Content Security Policy example

Loading/executing the script from is suppressed:

CSP warining message in Chrome Developer Tools

CSP media types

  • script-src: Execution of JavaScript
  • style-src: Same as script-src for style sheets.
  • connect-src: Limits connections via Ajax, WebSockets, etc.
  • font-src: Web fonts (e.g. TypeKit, Google Webfonts)
  • frame-src: Sources to be embedded via iframe (e.g. videos from YouTube or Vimeo)
  • img-src: External locations for images, …
  • media-src: … audio/video, …


You can specify sources by scheme (data:, https:), hostname (, fully qualified URI ( or wildcard (*://**)

  • 'none': Forbids a source type entirely
  • 'self': matches the current origin (but not subdomains)
  • 'unsafe-inline': allows inline JavaScript and CSS
  • 'unsafe-eval': allows JavaScript parsing with eval(), etc.

Warning: 'unsafe-inline' / 'unsafe-eval' will execute all
inline scripts, including possible XSS vectors.

A more complex example

jQuery CDN, Google web fonts, embedded YouTube videos

Header always set Content-Security-Policy: ↩
  "script-src 'self'; ↩
  style-src 'self'; ↩
  font-src; ↩
  img-src 'self' data:; ↩

Collect reports on what gets blocked

Header always set Content-Security-Policy: …↩
  report-uri /my/reporting/endpoint

You can also use for CSP data collection

Collect reports on what gets blocked

  "csp-report": {
    "document-uri": "",
    "referrer": "",
    "blocked-uri": "",
    "violated-directive": "style-src",
    "original-policy": "…"

Browser support for Content Security Policy

Google Chrome Mozilla Firefox Internet Explorer Opera Safari (OS X/iOS) Android Stock Browser
25 4+ 10 15+ 6.1+ 4.4

IE < Edge, some Android and/or older browsers may use
»X-Content-Security-Policy« or »X-Webkit-CSP«


Develop defensively

  • Strictly validate and sanitize all incoming data
  • Check for outdated/vulnerable dependencies
  • Make security part of your build process
  • Be aware of possible side channels (e.g. 3rd party scripts)

Security is always an ongoing effort.
Revise your tools and processes when necessary.

Thank you and stay safe!

Frederic Hemberger