Introduction

One of the most popular vectors of attacks on web applications is Cross-Site Scripting, called XSS. I will show you how the attack works and how to protect on some kinds of the attacks.

Why do you should be aware?

There are mostly three reasons why you should take care of it.

  1. Cookie theft – I will show a simple example in this article – the attacker will read the cookies from document.cookie variable and send it to a place where he can store it and get sensitive information like session id.
  2. Phishing – the attacker can modify the web page and, for example, show a fake login form.
  3. Keylogging – the attacker can attach to the addEventListener event and send all you type to his server. It is a very simple way to get passwords or credit card numbers.

How does it work?

The main goal of this kind of activity is to put the additional code on a page and execute it. In many cases, the code we want to add is a JavaScript script. Let’s start with a very simple example (written in PHP)

<?php header('X-XSS-Protection: 0'); ?>
<htm>
  <head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
  </head>
 
<body>
  <div class="container">
  <form method="get">
    <h1>Submit the PRO form</h1>
    <label for="title">Title:</label> <input type="text" id="title" name="title" class="form-control" /> <br />
 
    <textarea name="content" class="form-control"></textarea> <br />
 
    <input type="submit" title="submit" class="btn-primary" />
  </form>
 
  <?php
 
  if (count($_GET)) {
    echo "<h2>{$_GET['title']}</h2>";
    echo "<div>{$_GET['content']}</div>";
  }
 
  ?>
</div>
</body>
</html>

INFO: I added the X-XSS-Protection header to the response disabling default browser’s XSS protection. We do need it now.

As you can see, we have a regular form. If the data are sent – display them. The problem is when we add some HTML/JS code and the code will be displayed without any filtering to other users. It is a standard XSS attack!

We have two ways to solve the problem. First of all – escape the problematic characters. In PHP world we have htmlspecialchars function which does the magic.

The second solution is removing the HTML tags completely. There is a function for that called strip_tags which is designed for this purpose. There is one problem with it: it is not 100% HTML safe what may cause some minor bugs. Take a look at the code above:

<?php
 
$tag = '">Here it is<img src=img.jpg';
?>
<input type="text" value="<?=strip_tags($tag) ?>" />

the tag will be removed as we expected but the value of the input will be empty what’s not true

<input type="text" value="">Here it is" />

It may cause some other errors.

OK. It’s time for an example. I wrote working but a very ugly page in PHP which has no XSS protection.

<?php
header('X-XSS-Protection: 0');
 
session_start();
 
define('cookie_name', 'user');
define('admin_username', 'pro_admin');
define('admin_password', 'veeerySecurePasswordGoesHere');
 
function isLoggedIn()
{
    return !empty($_COOKIE[cookie_name]);
}
 
function checkCredentials()
{
    if (@$_GET['username'] == admin_username && @$_GET['password'] == admin_password) {
        $cookie_value = "John Doe";
        setcookie(cookie_name, $cookie_value, time() + (86400 * 30), "/");
    }
}
 
checkCredentials();
 
if (isLoggedIn()) :
    ?>
    <form>
        Search FBI database: <input type="text" name="search" value="<?=@$_GET['search'] ?>" />
    </form>
    <?php
 
else :
?>
 
<h1>Some awesome login form</h1>
 
<form method="get">
    <table>
        <tr>
            <td>Username: </td>
            <td><input type="text" name="username" value="<?=@$_GET['username'] ?>" /> </td>
        </tr>
        <tr>
            <td>Password: </td>
            <td><input type="password" name="password" value="<?=@$_GET['password'] ?>" /> </td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="Submit" /> </td>
        </tr>
    </table>
</form>
 
<?php endif;

I did some very bad things here like muting PHP warnings or sending data via GET method but I want to keep the example as simple as possible so… please forgive me!

When you run the example on your local machine using, for example, built-in web server.

$ php -S localhost:8888

you’ll see a simple login form. When you enter credentials pro_admin/veeerySecurePasswordGoesHere you’ll see a search form to browse FBI secret database.

Let’s say you logged in. Someone wants to use your credentials and log in. The attacker knows there is a simple form with a field named “search” which is vulnerable. He can send a link to the victim and read his cookies data. The link may look similar to the above:

http://localhost:8888/?search=%22%3E%3Cimg+src%3D%22fsfds%22+onerror%3D%22alert%28document.cookie%29%22+%2F%3E

The example output will be

My helpful screenshot

PHPSESSID=aeqcpdfcahs3n1o9it42dnbip5; user=John+Doe

And that’s all. You are hacked. You can enter the cookies to another browser or to a browser in incognito mode and you’ll see the secret FBI form. Nice, huh?!

Conclusion

This is only a simple example on how the attacks work and how this kind of vulnerable may be used. I hope you liked it.

About the author

Bartłomiej Kiełbasa

Bartłomiej Kiełbasa

Hi! I'm Bartek. I'm a PHP developer but other languages are not scary to me. My hobby is security and I try to learn as much as it's possible how to not be hacked. I'd ike to know how things work. After hours I like playing Dota2 and watching Dragon Ball :) If you will have any question, feel free to ask.