If you're a newbie when it comes to web development and security, it can be difficult to imagine exactly how a website gets hacked - When we develop a website, it only shows the code that we write - so how can someone run code on a machine that isn't theirs?

What's more, security is important. If your website is attacked, you need to protect your hard work, as well as your users data and privacy. How do you combat something you don't understand?

It's hard to discuss every possible vulnerability that you might see in a simple blog post - there is an entire field of research dedicated to it, so I'm going to give you an overview of a single type of attack, and how to prevent it.

What is a security vulnerability?

A security vulnerability can be explained as a weakness in a program or system that can be used to attack or breach a system. or breached.

An attack or exploit generally have one of two short-term outcomes:

  1. Get information from a system that the hacker wasn't supposed to get
  2. Cause the system to behave in a way that was not intended by developers (the golden goose being granting the hacker access to administrator privileges)

The extent of the damage caused by the breach can vary wildly, but a common goal is to steal sensitive information, gain control of the system, or to break a system. Sometimes these attacks can be very damaging, but others may only end up being annoying (as in the case of a Denial of Service Attack).

An example using XSS

One of the most common attacks that impact websites are XSS (cross-site scripting) attacks. The goal of an XSS attack is to inject JavaScript into someone else's website - which can be surprisingly easy to do.

Say I have a profile page at example.com/profile that displays my name to other users. It might have markup like this:

<body>
    <h1>
        Carl Anderson
    </h1>
    <section class="about-me">
        <!-- blah blah blah -->
    </section>
</body>

Since this is my profile, I'm allowed to change my name - so I change my name to <script>alert("Hello")</script>

This means that the HTML when for my profile now looks like this:

<body>
    <h1>
        <script>alert("Hello")</script>
    </h1>
    <section class="about-me">
        <!-- blah blah blah -->
    </section>
</body>

This is completely valid HTML, and since <script> tags will run anywhere on the page it now runs alert("Hello") every time someone visits my profile page.

But we're using <script> tags! It can do more than inject JavaScript inline... I can change my name to  <script src="/malicious/resource/here.js" />, which lets me load in as much JavaScript as I want.

This is the core of how an XSS attack works - I'm trying to inject my JavaScript onto someone else's page. The real danger here is that since my JavaScript is downloaded and run on the example.com domain, it's able to access its cookies - including your authentication tokens.

This means that once I get my JavaScript onto a website, I can do anything that JavaScript can do (like redirect people), but I can also do anything that you can do on that site.

The potential damage caused by one of these attacks depends largely on what the vulnerable website is for. If it simply stores your home recipes, it's probably nothing more than an annoyance; but if it stores your sensitive financial data, it can constitute a major breach.

Preventing an XSS attack

Unfortunately, stopping XSS attacks isn't as simple as it perhaps should be, since there are numerous ways an XSS attack can form. The key thing is that you should treat any user input as a threat, and you should not insert it anywhere on a page without first performing HTML entity encoding on it.

This works by changing special characters like < to &lt; - it still renders as <, but won't be interpreted as HTML tags.

PHP provides a default way of performing entity encoding through a function called htmlentities(). There is no JavaScript equivalent but if you want to use similar functionality, you can create our own:

function htmlEntities(str) {
    return String(str)
		.replace(/&/g, '&amp;')
		.replace(/</g, '&lt;')
		.replace(/>/g, '&gt;')
		.replace(/"/g, '&quot;');
}

The default way to  textContent field, which automatically escapes text before inserting it into the DOM.

let unsafeText = responseText;
let div = document.createElement('div');

// textContent automatically escapes the unsafe text
// innerHTML would insert it as HTML, and is vulnerable to XSS
div.textContent = unsafeText;
element.appendChild(div):

The key thing to note here is that inserting the same text through the innerHTML field does not escape the content, and is vulnerable to an XSS attack. Using innerHTML should be avoided when dealing with user-generated content.