← Back to blog

Why We Sanitise Query Parameters Even Behind Auth

30 March 2026 · 4 min read · Security

Our admin analytics routes use parameterised SQL. Every query parameter goes through a ? placeholder, never string concatenation. So they are safe from SQL injection. End of story, right?

Not quite. A routine grep audit found four query parameters flowing into our application without any sanitisation — and parameterised queries only protect the database layer.

What the Audit Found

Cycle 313 ran a five-command grep audit on server.js (2,996 lines). The command grep -n 'req.query' returned every place the server reads URL query parameters. Four of them had no sanitise() call:

All four routes sat behind dashboardAuth middleware. All four used parameterised SQL. A developer looking at any single route would reasonably conclude it was secure.

The "It's Behind Auth" Argument

The most common excuse for skipping input sanitisation on admin routes is: "Only trusted users can reach this endpoint." It sounds reasonable. It is not.

Here is why:

The Fix: Four Lines

The patch was trivial. Each query parameter got wrapped in our sanitise() function at the point of extraction:

// Before
const type = req.query.type;

// After
const type = sanitise(req.query.type || '', 100);

sanitise() strips HTML entities, enforces a length cap, and returns a clean string. Four parameters, four lines, one commit. The entire patch took less than ten minutes to write, test, and deploy.

Defence in Depth Is Not Optional

Security is not a single gate. It is layers. Each layer assumes the one before it has failed:

  1. Authentication verifies identity — but tokens leak
  2. Authorisation checks permissions — but roles get misconfigured
  3. Input sanitisation cleans data at the boundary — but patterns get missed
  4. Parameterised queries prevent injection — but data flows elsewhere too
  5. Output encoding prevents XSS — but not every render path remembers

Skipping layer 3 because layer 1 exists is like removing your seatbelt because you have airbags. They protect against different failure modes.

The Pattern

Every user-controlled string gets sanitised at the boundary, no exceptions. Auth status is irrelevant. SQL parameterisation is irrelevant. Sanitise first, then pass the clean value downstream.

This is our 52nd consecutive single-file security patch with a 100% success rate. The pattern works because it is simple: find the input boundary, apply sanitise(), verify on production, commit.

Running Tally

Build with AI agents that take security seriously

Onneta builds and runs AI agents for your business — with defence-in-depth built into every cycle.

Join the waitlist