---
title: Injection and escaping
summary: SQL injection and cross-site scripting are the same mistake in two places, data being read as code, and they have the same cure.
topic: security
order: 5
updated: 2026-09-19
---

## What it is

**Injection** happens when text from a user is glued into something that gets *executed*: a
SQL query, an HTML page, a shell command. If the text contains the right characters, it stops
being data and starts being instructions.

- In SQL it is **SQL injection**.
- In HTML it is **cross-site scripting (XSS)**: the user's text becomes a `<script>` that
  runs in other people's browsers.

## Why it is a rule

- **Heartland Payment Systems (2008).** A SQL injection on a web form was the way in. The
  attackers went on to steal around 130 million card numbers, one of the largest card
  breaches ever recorded.
- **TalkTalk (2015).** A UK telecom lost the details of about 157,000 customers to a SQL
  injection on old web pages. The regulator fined them £400,000 and called the attack one
  that could have been prevented.
- **The Samy worm (2005).** An XSS in MySpace profiles. It spread to over a million profiles
  in less than a day.

## How to do it

### SQL: parameters, always

```js
db.query(`SELECT * FROM users WHERE email = '${email}'`)

db.query('SELECT * FROM users WHERE email = $1', [email])
```

The first line is the bug. The second sends the query and the value separately, so the
database never reads the value as SQL, whatever it contains. There is no safe way to build
the first one by "cleaning" input.

Table and column names cannot be parameters. When one must vary, pick it from a fixed list
in the code, never from the request.

### HTML: escape on output

Every piece of user text that goes into HTML is escaped for the place it lands.

```js
const ESC = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }
const escape = (s) => String(s).replace(/[&<>"]/g, (c) => ESC[c])
```

- Between tags and inside a quoted attribute, this is enough.
- Inside a `<script>`, a `href`, a `style` or an unquoted attribute it is **not**. Keep user
  data out of those, or use an encoder made for that context.
- Frameworks that escape by default (React, most template engines) are safe until someone
  uses the escape hatch (`dangerouslySetInnerHTML`, `{{{ }}}`, `innerHTML`).

### Escape first, then format

A Markdown or rich-text renderer should escape the whole source **before** turning `**` into
`<b>`. Then a `<script>` typed into the source is already harmless text by the time any
pattern sees it. Doing it in the other order turns the renderer into an injection door.

### Links need a scheme check

`<a href="javascript:…">` runs code when clicked, and escaping does not change that. Allow
only `https:`, `http:`, `mailto:` and relative links.

### Content-Security-Policy as the backstop

A CSP header tells the browser which scripts may run. If an injection gets through, a strict
CSP can stop the script from executing. It is a second wall, not a replacement for escaping.

## How we do it here

Every database call is parameterised. Every page renderer escapes before it formats, and link
targets are limited to safe schemes. This site's Markdown renderer, the one drawing this
page, escapes the whole source first.

## Benefits

- Parameterised queries remove SQL injection completely, not partly.
- Escaping on output protects every page that shows the data, including ones written later.
- Both are cheap: no measurable cost.

## Disadvantages

- Escaping has to be right for each context, and the HTML-body escape is wrong inside
  scripts and URLs.
- Dynamic SQL (sort columns, optional filters) needs allow-lists written by hand.
- A strict CSP breaks inline scripts and many third-party widgets until they are reworked.

## Checklist

- No string-built SQL anywhere.
- User text is escaped at the point it enters HTML.
- Renderers escape before they format.
- Links allow only safe schemes.

## Sources

- [OWASP — SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)
- [OWASP — Cross Site Scripting Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)
- [MDN — Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
