How databases and web applications work together

To understand SQL injection, you first need a basic picture of how most web applications store data. Behind almost every website — a shop, a blog, a booking system, a social network — there is a database. That database holds user accounts, product listings, orders, comments, and everything else the site needs to function.

When you log in, search for something, or load your profile, your browser sends a request to the server. The server then constructs a database query in a language called SQL (Structured Query Language) and sends it to the database to retrieve or update the relevant information. The database runs the query and sends the results back to the server, which then formats them into the page you see.

A login query might look something like this:

SELECT * FROM users WHERE username = 'alice' AND password = 'mypassword';

If the database finds a row matching that username and password, the server treats it as a successful login. This is completely normal and how millions of applications work. The problem arises when the username and password values in the query are taken directly from what the user typed, without any checking or cleaning.

How SQL injection works

SQL injection happens when an attacker types something into a form — or includes something in a URL — that breaks out of the expected data and becomes part of the SQL command itself.

Consider a vulnerable login form. The server builds the query by concatenating strings:

// The server builds this query using what the user typed:
$query = "SELECT * FROM users WHERE username = '" . $username . "' AND password = '" . $password . "'";

// Normally, if username is "alice" and password is "mypassword", you get:
SELECT * FROM users WHERE username = 'alice' AND password = 'mypassword';

Now watch what happens if an attacker types admin'-- as the username and anything as the password:

// The resulting query becomes:
SELECT * FROM users WHERE username = 'admin'--' AND password = 'anything';

// The -- starts a comment in SQL. The database sees:
SELECT * FROM users WHERE username = 'admin'; -- ' AND password = 'anything' (ignored)

The password check has been completely removed from the query. The database finds the admin user and returns a match — so the server logs the attacker in as admin without them ever knowing the password.

Authentication bypass

The login bypass shown above is just the simplest example. SQL injection can be used to extract entire databases, modify or delete records, bypass all authentication, and in some server configurations, execute commands on the operating system itself.

What attackers can do with SQL injection

The impact of SQL injection varies depending on the database setup, the level of permissions the application has, and how much the attacker knows about the database structure. Here are the most common outcomes:

  • Dump entire databases: Attackers can extract all data from all tables — user accounts, passwords, emails, payment details, private messages, and anything else stored in the database.
  • Authentication bypass: Log in as any user, including administrators, without knowing their password.
  • Modify or delete data: Change prices in a shop, delete records, alter account balances, or corrupt critical business data.
  • Read files from the server: On some database configurations, SQL injection can be used to read arbitrary files from the server's file system.
  • Write files to the server: In some cases, an attacker can write files — including malicious scripts — directly to the web server through the database.
  • Execute operating system commands: In the most severe cases, using stored procedures or other database features, an attacker can run commands directly on the server's operating system.

How SQL injection is detected

Attackers and automated security scanners typically detect SQL injection by submitting test inputs that are designed to cause database errors or produce unexpected output. A single quote character (') is one of the most common probes — because in a vulnerable query, it breaks the string syntax and causes a database error.

If a form or URL parameter returns an error like You have an error in your SQL syntax near '''', that is a strong indicator of a SQL injection vulnerability. Verbose error messages make the attacker's job much easier by revealing the database type and sometimes even the query structure.

More sophisticated injection techniques — called "blind SQL injection" — work even when the application does not show error messages. The attacker asks the database true-or-false questions and infers the answer from subtle differences in the application's response, such as whether a page loads slightly faster or slower.

How to prevent SQL injection

SQL injection is one of the best-understood vulnerabilities in web security, and the primary fix is straightforward. The core problem is that user input is being mixed with SQL commands. The solution is to keep them permanently separate using a technique called parameterised queries (also called prepared statements).

Parameterised queries (prepared statements)

A parameterised query sends the SQL command and the data values to the database separately. The database receives the query structure first — with placeholders for the data — and then receives the actual values. Because the values arrive after the query structure has already been parsed, they can never be interpreted as SQL commands. No matter what the user types, it is always treated as plain data.

// VULNERABLE: string concatenation — never do this
$query = "SELECT * FROM users WHERE username = '" . $username . "' AND password = '" . $password . "'";

// SAFE: parameterised query using PDO (PHP)
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);

// SAFE: named parameters — equally good
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :user AND password = :pass");
$stmt->execute([':user' => $username, ':pass' => $password]);

Every modern programming language and database driver supports parameterised queries. There is almost never a good reason to build SQL queries using string concatenation when parameterised queries are available.

Use an ORM

Object-Relational Mappers (ORMs) like Laravel Eloquent, Django ORM, SQLAlchemy, and Hibernate automatically use parameterised queries behind the scenes. When you use ORM methods to query the database rather than writing raw SQL strings, you are protected from injection by default. This is one reason why working with a well-maintained ORM is generally safer than writing raw SQL for every database interaction.

Limit database permissions

Even if an injection vulnerability exists, you can limit the damage by ensuring the database user your application connects with has only the permissions it needs. An application that only reads from the database should not use an account that can delete tables. If an attacker exploits an injection vulnerability in a read-only account, they can view data but cannot modify or destroy it.

Never display database errors to users

Detailed database error messages should never appear in production. They reveal the database type, the query structure, and sometimes column and table names — all of which make an attacker's job significantly easier. Log errors server-side and display a generic error message to users.

Scan your site for SQL injection

ScanexAI tests your web application for SQL injection by analysing error messages and response patterns across your pages and API endpoints. Run a free scan to check for injection vulnerabilities.

Summary

SQL injection happens when user-supplied input is mixed with SQL commands, allowing an attacker to alter the structure of the database query itself. The consequences range from authentication bypass and credential theft to the complete extraction or destruction of a database. It is one of the most reliably devastating vulnerabilities in web security, but also one of the most preventable.

The fix is to always use parameterised queries or a trusted ORM. Keep user input and SQL commands separate at all times. Restrict database permissions to the minimum needed. Never display verbose database errors in production. These steps eliminate virtually all risk of SQL injection in a standard web application.