Notes /

MySQL, express, and the importance of types

Written last month (March 29, 2025)

TLDR object serialization via sqlstring , vulnerable if user input is not primitive (e.g. unchecked object).

const app = express()
const db = await mysql.createConnection({ ... })

app.get("/", (req, res) => {
    // unsafety here :)
    let users = await db.query("SELECT * FROM table WHERE name = ?", [req.query.name])
    res.json(users)
})

There’s no way to prevent this, says only language where this regularly happens

SQL Injection is bad! Use a library to prevent SQL Injection!

Nowhere in MySQL2 docs does it mention that objects are serialized into SQL (Note that this may have changed - this is more of a guide for testing vulns / ctf challenges than a bug report, this is not new research). Via dependencies, you can find it uses sqlstring . That does mention that it does object serialization:

var post = {id: 1, title: 'Hello MySQL'};
var sql = SqlString.format('INSERT INTO posts SET ?', post);
console.log(sql); // INSERT INTO posts SET `id` = 1, `title` = 'Hello MySQL'

But wait! Query is surely just string?
No

Express parses query string like name[obj]=val. And MySQL accepts 1 = 1 = 1. Also, people often allow both urlencoded and json as input, even if their pages use urlencoded forms, so try submitting a JSON body with a similar payload.

This is based on previous research and solving a CTF challenge. I get reminded of it every few months by reading another project which uses mysql2 and express. I do not support using express from a technical view - it is full of surpises, took really long to get promises support (so long that most libraries based on express still don’t support it), and is actually the slowest of the commonly used frameworks.


Daniel Bulant - Blog posts CC-BY-SA (unless otherwise specified)