Let's Talk about Cross-site Scripting (XSS)

2019-08-16 • 4 min read
#xss#Cross-site Scripting#security#web security series

One of my colleagues is passionate about web security. Recently, we asked him if he could share his security knowledge with us. And therefore we have bi-weekly security sharing session now :D I'm going to document what I've learned from him by a series of blog posts.

The first topic in this series is Cross-site Scripting (XSS). According to Hackerone, it is in the top 10 list of most impactful vulnerability types. A simple way to explain XSS is that your web application allows untrusted (and usually malicious) script execution.

XSS Types

XSS is usually classified as two types: Stored XSS and Reflected XSS.

Stored XSS

Stored XSS is a more severe type of XSS. It happens when server trusts (malicious) users' input and store it on server. Without proper HTML escaping, it will lead to permanent display on normal pages for all other users.

Below is an example, how does it cause XSS vulnerability? Think about it for a couple minutes before I explain it 😄

  onIgnoreTag: function (tag, html, options) {
    // allow comment tag
    if (tag === '!--') {
      // do not filter its attributes
      return html;

The above code snippet attempts to directly return html if it is not in comment tag (e.g., <!-- comment -->). It seems pretty intuitive to do so. However, suppose html is a malicious attribute some bad guy stored on our server, then the following thing could happen. Say, if html is

<!-- hack=" --><script>alert(1)</script>//s" -->

<!-- hack=" --> part will be looked as comment tag and be ignored. And The latter part <script>alert(1)</script>//s" --> will be returned as html directly. Whoever open this webpage will then trigger the malicious script <script>alert(1)</script> 😱

To avoid this happening, we should implement some HTML escape check. (See the fixing example here).

Reflected XSS

Another type of XSS is Reflected XSS. It is by far the most basic type of web vulnerability. Reflected XSS allows attackers to inject a script into the application. The malicious script is used immediately by server-side scripts, without properly sanitizing the content. When attackers send a malicious link to a user, the one who clicks on the malicious link will be affected and trigger the script.

Below is an example from here (there are more examples in the link):

http://vulnerabledoma.in/bypass/str_literal?q= is a link that accepts some query parameters. If you go to that website and check its html, what it basically does is the following: taking whatever you send on q and set it to the following script.

<script>var q=""</script>

So, if you click on the link http://vulnerabledoma.in/bypass/str_literal?q=hello. Then the page will be like this:

<script>var q="hello"</script>

What if an attacker sends http://vulnerabledoma.in/bypass/str_literal?q=%22%3Balert(1)// to someone? The webpage will be the following:

<script>var q="";alert(1)//"</script>

If you click on the link and go to that page, it will execute alert! 😱

How to Prevent XSS?

It is very hard to completely prevent XSS happening. Yet, we still have some rules we can follow to prevent it.

  • Sanitize function input.
  • Sanitize function output.
  • Content-Security-Policy (CSP)

The first two rules can refer to the examples of stored XSS and reflected XSS above. There are more details of how you can do to prevent XSS with CSP here. You should be very careful to choose which origin to whitelist with CSP.

Consider the following example that you trust google.com and whitelist it:

Content-Security-Policy: "default-src 'google.com'"

It seems not harmful, no?

However, there are a lot of websites supporting JSONP, and google.com is one of them. JSONP is a JavaScript pattern to request data by loading a <script> tag that allows users to add function before json callback. If you trust google.com and whitelist it, the following thing might happen.

<script src="https://www.google.com/jsapi?callback=alert"></script>

You trust google.com and attackers put malicious script in the JSONP callback, then your webpage will still execute alert from the above example.

What if you only trust yourself with CSP?

Content-Security-Policy: "script-src 'self'"

This alse seems quite intuitive, no? Yet, if an attacker uses dataurl as a script source like below, it will be considered as the same origin.

<script src="data:application/javascript;charset=utf-8;base64,YWxlcnQoJ1hTUycpOw=="></script>
The best way to prevent XSS is "Don’t trust anyone!"

Try it out!

There is small XSS game for you to try on here if you want to get your hands on using simple technique to execute XSS attack :p

Again, thank my colleague for sharing these concepts.

This is the 1st post of the Web Security Series. In the next post, we will talk about CSRF. (To be continued...)