Skip to content

Test your website now

Free, no card. We read only public pages.

Run free audit →

Security

What is SQL injection, and how do you prevent it?

SQL injection lets an attacker run their own database queries through your app. How it works, the fix that prevents it, and where it still appears today.

SQL injection is an attack where user input is placed directly into a database query, so an attacker can change the query: read other users' data, bypass login or delete records. The fix is to never build SQL by concatenating strings. Use parameterized queries or a query builder, and give the app's database user only the permissions it needs.

Published · Last updated

By Michal Měřínský. Audit methodology.

How it works

Imagine a login query built from text:

const sql = "SELECT * FROM users WHERE email = '" + email + "'";

If someone types a value containing a quote and extra SQL, that text becomes part of the query. The database cannot tell your code from the attacker's input, because they arrived as one string. Depending on the query, an attacker can return every row, skip the password check or run additional statements.

The fix: parameterized queries

Send the query and the values separately, so the database treats input as data, never as code:

const { rows } = await db.query("SELECT * FROM users WHERE email = $1", [email]);
  • ORMs and query builders such as Prisma, Drizzle and Supabase's client parameterize for you, as long as you use their normal methods.
  • Validate input types and lengths as a second layer, not as the main defence.
  • Give the app's database user only the permissions it needs: no schema changes, no access to unrelated tables.
  • Do not show database errors to users. They help attackers refine an injection.

Escaping quotes by hand is not a fix. Parameterization is.

Where it still appears

  • Raw SQL escape hatches in ORMs, such as queryRaw with string interpolation, used for a complex report.
  • Database functions that build dynamic SQL from their arguments, for example a Postgres function called through an RPC endpoint.
  • Sorting and filtering by column names taken from the URL, which cannot be parameterized and must be checked against an allowlist.
  • AI-generated code. Generated database code sometimes concatenates input for convenience, especially in admin tools and scripts. Review every place where text and SQL meet.

Supabase and row level security

With Supabase, the browser talks to the database through an API using a public key. Injection through the normal client is not the main risk there. The main risk is missing row level security, which lets anyone with the public key read or change rows they should not. Enable RLS on every table and write policies before launch.

What a scan can and cannot tell you

WebAuditOS does not attempt attacks, so it cannot detect SQL injection. It checks for the information that makes attacks easier: error pages that expose stack traces or database errors, and headers that reveal exact server and framework versions.

Finding injection requires code review, dependency and static analysis, or an authorized penetration test.

No passive scanner can prove the absence of SQL injection. Anyone who claims otherwise is guessing.

Short answers

Do ORMs prevent SQL injection? Yes, when you use their parameterized methods and avoid raw string queries.

Can a WAF stop SQL injection? It blocks common patterns. It does not fix the vulnerable code.

Is NoSQL immune? No. NoSQL databases have their own injection problems.

How do I test my app? Review every query that includes user input, and use an authorized security test.

In the product

The checks this maps to

Every check named here runs on every scan, on every plan - what a plan changes is how many pages are crawled, how long the history is kept and what you can do with the result. These are the ids the findings carry, so you can match a line in a report to the explanation above.