Digital self-defense

Mitigate Clickjacking and XSS attacks with HTTP headers

Why should we deal with security at all?

All sorts of data depict our (digital) existence

  • The same password is often used for various services
  • Many people live and share their life online: personal information, online profiles, addresses, contacts and friends, credit cards/banking information, personal photos, texts, feelings.

But the most important reason:

  • You are asking you users for all this data …
  • … to offer your services, for marketing purposes, etc.
  • Your users trust in you.
  • Therefore, it's your duty to deal carefully with it.

Two examples of security problems on websites


„Kidnapping“ of mouse clicks to perform actions on another page


Attacking website

What is happening here?

The attacker embeds the target website into an absolute positioned iframe:

iframe {
    width: 850px;
    height: 370px;
    position: absolute;
    top: -198px;
    left: -424px;

    /* 50% opacity only for demonstration. */
    filter: alpha(opacity=50);
    opacity: 0.5;

The same page with opacity of the iframe set to 0.

Possible attack scenarios

  • Changing privacy settings
  • Deleting data or entire accounts
  • Triggering one-click orders, etc.

Even multiple steps (e.g. confirmation dialogs) can be bypassed.

Remedy: X-Frame-Options (HTTP header)

  • X-Frame-Options: SAMEORIGIN
    Same-origin-policy: Protocol (such as http://) and (sub)domain must be identical
  • X-Frame-Options: DENY
    Disallow embedding the page entirely
  • X-Frame-Options: ALLOW-FROM origin
    Embedding is only allowed from a specific domain. Only implemented in Firefox 18+/IE9+, will be solved differently in the future

Browser support X-Frame-Options (DENY/SAMEORIGIN)

  • Chrome 4.1+
  • Firefox 3.6.9
  • Safari 4+
  • Internet Explorer 8+
  • Opera 10.50+

Adding HTTP headers: Apache webserver

.htaccess or server configuration

<IfModule mod_headers.c>
    Header always set X-Frame-Options "SAMEORIGIN"

Adding HTTP headers: IIS


<?xml version="1.0" encoding="UTF-8"?>
                <add name="X-Frame-Options" value="SAMEORIGIN"/>

Cross Site Scripting (XSS)

Unintentional execution of (malicious) JavaScript code


If it's possible for an attacker to run this sample code,
any arbitrary JavaScript code can be executed on the page as well

Reflected XSS attacks are often delivered by other websites:<script>alert(1)</script>

Example for a DOM XSS attack

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

Stored XSS attacks are saved on the server
and then delivered to every visitor of the site.

How do XSS attack vectors look like?

(just a very small excerpt)

(Source: HTML5 Security Cheatsheet)

<​frameset onload=alert(1)>
<​body oninput=alert(1)><​input autofocus>
<​video poster=javascript:alert(1)//></video>
<!--<img src="--><​img src=x onerror=alert(1)//">

How can these types of attack be mitigated?

  • Validate all kinds of user input carefully
  • Not just form input, but also values of GET parameters
  • XSS code can also be injected through side channels
    (e.g. SQL injection)
  • Avoid HTML input where possible

/Everybody stand back/

I know Regular Expressions!

XKCD #208

Filtering input with Regular Expressions

/<[^>]*>?/g removes all HTML tags.

  • … but unfortunately also a < b
  • You may alter the user's input in an unintended way
  • Different representations of < and > stay intact, for example:
    &lt; &gt; %3C %3E

This may be a valid solution, but it's not 100% perfect.

But what if you can't go without HTML?

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

Obviously not a good idea:


Idea #2:
Filter out HTML in the front-end with JavaScript

As seen many times on StackOverflow™

// never, never, NEVER use this!
function stripHtmlWithXss(html) {
  var tmp = document.createElement("DIV");
  tmp.innerHTML = html;
  return tmp.textContent || tmp.innerText;

// never, never, NEVER use this! (part 2)
function jQueryStripHtmlWithXss(html) {
  return $('<div/>').html(html).text();

// … or jQuery's own parsing function

// Small change, but a safer solution
function stripHtmlSafe(html) {
  var tmp = document.implementation.createHTMLDocument();
  tmp.body.innerHTML = html;
  return tmp.body.textContent || tmp.body.innerText;

  • Patch for jQuery 1.12 and 2.2: $.parseHTML() will use document.implementation.createHTMLDocument() in the future.

    Result: XSS code won't be triggered immediately any more …

  • … but only if you insert those DOM nodes again without filtering.
  • Other DOM-based attacks (e.g. DOM Clobbering) are still possible
  • But we have now time to react: DOM Purify

Filtering correctly is hard

XSSed - Top sites by pagerank

  • ASCII control characters, Unicode symbols, escaped sequences, etc.
  • Tools like DOM Purify (JS), HTML Purifier (PHP) or
    AntiSamy (Java, .NET) help.
  • Never output any data entered by the user just as you received it – always escape it instead
  • Think about the context to choose the proper escapement: HTML, CSS, JSON, etc.

Some browsers help you with reflected XSS

But you should never rely exclusively on the browser

Recap: XSS

  • Mistrust all user input in general (each and every bit)
  • All input must be validated carefully (check length, type and format)
  • Be especially careful with HTML (e.g. comment forms, WYSIWYG editors)
  • If required, explicitly allow a very limited set of HTML tags (Whitelisting)
  • Markdown is a good alternative, also for your clients
  • Additionally, check characters and their encoding:
    (escaped) control chars, Unicode, etc.
  • »Escape your input, encode your output.«

(Un)invited guests

Prevent execution of arbitrary scripts

  • Filtering is hard, something might have slipped through
  • The problem doesn’t even need to exist on your own server
  • Most websites load many different external resources:
    web fonts, scripts from CDN servers, ads, analytics, tracking pixels, etc.
  • All those sources pose a potential security issue as well
January 3, 2014: Malicious advertisements served via Yahoo

Remedy: Content-Security-Policy

Whitelisting of allowed external sources

Simple example for CSP


<!DOCTYPE html>
    <script src=""></script>
    <script src=""></script>

Simple example for CSP


<IfModule mod_headers.c>
    Header always set Content-Security-Policy: ↩
      "script-src 'self'"

Simple example for CSP

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, …
  • object-src: … Flash and other plug-ins.

Starting CSP version 1.1:
frame-ancestors: Which domains are allowed to embed the current site via iframe?


  • '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.

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

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

A more complex example for CSP

jQuery CDN, Google web fonts, Tumblr images, embedded YouTube videos

<IfModule mod_headers.c>
    Header always set Content-Security-Policy: ↩
      "script-src 'self'; ↩
      style-src 'self'; ↩
      font-src; ↩
      img-src 'self' data: https://*; ↩
      frame-src; ↩
      object-src 'none'"

Browser support Content-Security-Policy

  • Chrome 25
  • Firefox 4+ (versions below 23 use »X-Content-Security-Policy«)
  • Safari 6.1+
  • Opera 15+
  • Internet Explorer 10 (limited)

The bottom line:

  • „Security by default“: Already think about security during development
  • Clickjacking: Use embedding protection
  • XSS: Always mistrust all sorts of user input: Validate and filter carefully
  • CSP: Prevent unwanted script execution
  • Keep in mind that some proxies might alter/remove HTTP headers
  • When you're working with sensitive data (e.g. personal or payment data), have a security audit on a regular basis

And most important:

There is no such thing as total security. You have to stay on guard!

That's all folks!


Frederic Hemberger (@fhemberger)

Links and further resources



Cross-Site-Scripting (XSS)

Content Security Policy (CSP)

Security in general