Olympus Docs
SecurityWeb attacks

SQL injection prevention

How Olympus's internals avoid it, and what to watch in your apps

SQL injection happens when user input is concatenated into an SQL string and changes the query's meaning. The fix is parameterized queries, always.

Olympus's posture

All Olympus-authored code uses parameterized queries. Specifically:

Kratos and Hydra

Go services using database/sql with placeholders ($1, $2). Reviewed and audited upstream.

Athena backend

Uses postgres (npm) with tagged-template queries:

await db`SELECT * FROM identities WHERE id = ${id}`;

The library parameterizes, id is never inlined into the SQL string.

Custom hooks / webhooks

If you write a webhook backend (in any language), follow your driver's parameterization. Examples:

Node.js (pg):

await client.query("SELECT * FROM users WHERE email = $1", [email]);

Python (psycopg2):

cur.execute("SELECT * FROM users WHERE email = %s", (email,))

Go (database/sql):

db.QueryRow("SELECT * FROM users WHERE email = $1", email)

What you'll see in audit logs

Suspicious patterns:

  • ' OR 1=1--
  • '; DROP TABLE
  • UNION SELECT
  • Excessive single quotes / semicolons in form fields

Olympus's audit logs capture failed-login traits with these patterns. Worth a dashboard alert.

ORMs and "safe by default"

ORMs (Drizzle, Prisma, SQLAlchemy, ActiveRecord) parameterize by default. But:

Raw SQL escapes

db.execute(sql.raw("...${userInput}")) is unparameterized. Don't.

LIKE patterns

// Vulnerable to LIKE injection (not SQL injection per se, but enumeration):
await db`SELECT * FROM users WHERE email LIKE ${'%' + search + '%'}`;

User input 100% causes a wildcard match. Sanitize: escape % and _ in user input.

const escaped = search.replace(/[%_]/g, ch => "\\" + ch);
await db`SELECT ... WHERE email LIKE ${'%' + escaped + '%'} ESCAPE '\\'`;

Dynamic column names

You cannot parameterize identifiers (table names, column names):

const col = req.query.sortBy; // attacker-controlled
await db`SELECT * FROM users ORDER BY ${col}`; // FAILS, column injected

Allow-list valid column names:

const allowed = ["created_at", "email", "id"];
if (!allowed.includes(col)) throw new Error("invalid_sort");
await db`SELECT * FROM users ORDER BY ${sql(col)}`; // sql() inlines safely

Time-based attacks

Even with parameterization, application logic can leak via timing:

const user = await db`SELECT * FROM users WHERE email = ${email}`;
if (!user) return res.status(401).json({ error: "user_not_found" });
const ok = await bcrypt.compare(password, user.hash);
if (!ok) return res.status(401).json({ error: "wrong_password" });

Different errors → attacker enumerates valid emails. Best practice: same error, same timing (do a fake bcrypt compare even when no user).

Database user permissions

Run the app as a DB user with least privileges. Olympus's kratos, hydra, athena users own only their schemas, they can't drop other schemas even if injected.

Read-only replicas for read-heavy app code reduce blast radius further.

On this page