CTEC2712 Web application development

Security concerns

SQL injection & Cross-site scripting

Contents

  1. Entry points
  2. Danger points
  3. SQL injection
  4. Cross site scripting
  5. Escaping with HTML character references

Entry points

Users provide data through the HTTP request in a number of ways.

Via search parameters in the URL like /mypage?anything=danger.

1
2
const url = new URL(request.url);
const danger = url.searchParams.get("anything");

Via named groups in the URL like /users/danger.

1
2
3
const pattern = new URLPattern({pathname: "/users/:anything"});
const match = pattern.exec(request.url);
const danger = match.pathname.groups.anything;

Via form data in POST requests.

1
2
const formData = await request.formData();
const danger = formData.get('anything');

If we trust this user data implicitly, then we make our system vulnerable to attack. ALL user data should be considered tainted.

1

Danger points

So, how can data do harm to my application?

SQL injection is where users are able to execute arbitrary SQL code on our database. This can lead to data being lost or stolen, including potentially sensitive user data.

1
2
3
4
5
6
// user data can be malicious SQL fragments like this
const malicious = "'); DELETE FROM items; --";

// If we execute the code, we lose our data.
const query = `INSERT INTO items (label) VALUES ('${malicious}');`;
db.exec(query);

Cross-site scripting is where users are able to inject arbitrary HTML code into the response. This potentially allows for user-submitted JavaScript code to be executed on user machines.

1
2
3
4
5
6
7
8
9
// user data can be HTML containing malicious JavaScript.
const malicious = `<script>alert('executing arbitrary code!');</script>`;

// We naively build it into our response.
const content = `<p>Username: ${malicious}</p>`

// If we return an HTML response, it will execute on the users machine.
const headers = new Headers({"content-type": "text/html"});
return new Response(content, { headers });

We need to protect our application from malicious code.

2

SQL injection

SQL injection in detail

User enters data - we can’t stop this.

1
2
3
4
<form method="POST">
    <label for="data">A necessary danger: </label>
    <input id="data" name="unavoidable">
</form>

We take the data from the request.

1
const unavoidable = formData.get("unavoidable");

The problem occurs when we construct our SQL code using the malicious data.

1
2
3
4
const oops = "'); DELETE FROM items; --";

// BAD!
db.exec(`INSERT INTO items (label) VALUES ('${oops}')`);

If we use prepared statements then the database engine will sanitise the inputs for us.

1
2
3
4
const noProblem = "'); DELETE FROM items; --";

// SAFE!
db.prepare(`INSERT INTO items (label) VALUES (?)`).run(noProblem);

Protect the database. Use prepared statements!

3

Cross site scripting

Cross site scripting in more detail.

User enters data - we can’t stop this.

1
2
3
4
<form method="POST">
    <label for="data">A necessary danger: </label>
    <input id="data" name="unavoidable">
</form>

We take the data from the request.

1
const unavoidable = formData.get("unavoidable");

The problem occurs when we construct our HTML code using the malicious data.

1
2
3
4
5
// user data can be HTML containing malicious JavaScript.
const oops = `<script>alert('executing arbitrary code!');</script>`;

// We naively build it into our response.
const content = `<p>Username: ${oops}</p>`

Add the @std/html module

1
deno add jsr:@std/html

We can fix this by escaping the user provided data

1
2
3
4
5
6
7
// The escape function will protect us
import { escape } from '@std/html';

const noProblem = `<script>alert('executing arbitrary code!');</script>`;

// escaping malicious user data makes it harmless.
const content = `<p>Username: ${escape(noProblem)}</p>`

Users are dangerous, don’t trust them.

4

Escaping with HTML character references

Special characters such as < and > are meaningful to the HTML parser.

HTML named character references are escape sequences that render as special characters in HTML.

Character referenceRenders as
&copy;©
&amp;&
&lt;<
&gt;>

Passing strings through the escape function converts these active HTML characters into harmless character references.

1
2
3
4
5
import { escape } from '@std/html';

const badString = "<p>Nasty</p>";
const safeString = escape(badString); 
// %lt;p&gt;Nasty%lt/;p&gt;

Keep safe, escape user content.

5

Security concerns

If you have any questions, now is a good time to ask.

Thanks for listening