HolyGhost logoHolyGhost
← cd ..
Analysis

SQL Injection: When Your Input Becomes the Database's Command

SQL injection turns a login box or search field into a way to read, change, or destroy a database. Here is how it works, the flavours it takes, and the one fix that actually stops it.

HolyGhost··10 min read

Picture a small online shop. A customer types their name into a login box, hits enter, and the site checks them against its records. Nothing unusual. Now picture a different visitor who does not type a name at all. Instead they type a short burst of punctuation and a stray word or two, and the site swings open like an unlocked door. No password. No alarm. The visitor is suddenly reading every customer record the shop has ever stored. Nobody kicked in a server. The attacker just spoke the database's language into a box that was only ever meant for a name.

That is SQL injection, and it is one of the oldest tricks in web security. It is also still one of the most damaging. The idea is that a field meant for your name or a search term gets treated as part of a database command instead of as plain data. When that happens, the attacker is no longer filling in a form, they are writing queries. This is how it works and how it is stopped.

Scope

SQL injection is a long documented vulnerability class, discussed publicly since the late 1990s. This explains it for defenders and learners, on systems you own or are authorised to test.

First, what is SQL?

Before we break anything, a quick plain language definition. SQL stands for Structured Query Language. It is the language most applications use to talk to their database. A database is just an organised store of information, think of it as a giant, very fast set of spreadsheets called tables, where each table holds rows of records (users, orders, products) and each row has columns (a username, an email, a price).

When you use a website, you almost never touch the database directly. Instead the application writes SQL on your behalf and sends it off. A query is one of these commands. A simple one reads like an instruction in slightly stiff English:

SELECT email FROM users WHERE username = 'alice'

That says: from the users table, give me the email column, but only for the row where the username is alice. The database reads it, runs it, and hands back the answer. Everything works beautifully as long as the database can tell the difference between the fixed instruction and the piece of data you supplied. SQL injection is what happens when it cannot.

The core idea

Most applications build database queries by combining fixed SQL with user input. The danger is when the input is glued straight into the query text, a habit called string concatenation, which just means sticking two pieces of text together end to end. Consider a login check built like this:

SELECT * FROM users WHERE username = 'INPUT' AND password = 'INPUT'

The application means for your typed username to sit neatly inside those single quotes, as a harmless label. But if it simply pastes your input in without care, an attacker can type a username of admin' -- and change the meaning of the whole command. Here is what the database actually receives:

SELECT * FROM users WHERE username = 'admin' --' AND password = '...'

Look closely at what happened. The attacker's own single quote closed the username string early. Then --, which starts a SQL comment, told the database to ignore everything after it on that line. A comment is a note for humans that the database skips over, so the entire password check has just been thrown away. The database now cheerfully returns the admin row, and the attacker is logged in as admin without ever knowing the password.

The input stopped being data and became part of the command. That single sentence is the whole vulnerability. Everything else is variations on it.

An easy way to picture it: imagine you hand a builder a note that says "hang the picture where I marked the wall". Harmless. Now imagine someone slips extra words onto your note so it reads "hang the picture where I marked the wall and also give this stranger the house keys". The builder cannot tell your instruction from the smuggled one, so they do both. The database is that trusting builder.

The flavours

SQL injection comes in a few forms, and they are grouped by how the attacker gets the answer back out. Understanding this matters because it shapes how an attack is spotted and how loud it is.

  • In band. The results come straight back in the page, in the same channel the attacker is already using. This includes error based injection, where the database's own error messages are provoked into leaking data (a badly configured site that prints raw errors to the screen is doing the attacker a favour), and union based injection, which uses SQL's UNION keyword to bolt the attacker's own query results onto the results the page was going to show anyway. UNION simply stitches the rows of two queries together, so an attacker uses it to append, say, every password onto a product listing.
  • Blind. The page shows no data and no errors, so the attacker cannot read answers directly. Instead they ask the database yes or no questions and infer the answer from how the application behaves. Boolean based injection watches whether the page changes when a condition is true versus false (does the result appear, or vanish?). Time based injection asks the database to pause for a few seconds if something is true, then measures the delay. If the page hangs for five seconds, the answer was yes. It is slow and patient, like a burglar tapping a wall and listening for the hollow spot.
  • Out of band. The database itself is made to reach out and send data to a server the attacker controls, for example by triggering a network lookup that smuggles stolen data into the request. This is useful when nothing comes back through the normal page at all.

Blind does not mean safe

People sometimes assume that if a site shows no error messages, it is protected. Not so. Blind SQL injection extracts data one true or false question at a time, and automated tools do this fast. Hiding error messages is good hygiene, but it only removes one convenience for the attacker, not the vulnerability.

What it lets an attacker do

Once you can inject SQL, the database is largely yours. The specific damage depends on what the application's database account is allowed to do, but the range is wide:

  • Read any data the database account can see: usernames, password hashes, email addresses, order histories, whole customer records.
  • Bypass authentication, exactly as in the login example above, walking straight past the password check.
  • Modify or delete data, changing prices, granting themselves admin rights, or wiping tables entirely.
  • Sometimes reach the operating system. Depending on the database product and how much privilege its account has, an attacker can occasionally run commands on the underlying server itself, turning a web flaw into full control of the machine.

To make the stakes concrete, here is a rough sense of scale for a single vulnerable field:

One injectable search box  ->  read the entire users table
                           ->  dump password hashes for offline cracking
                           ->  alter an order, refund it to themselves
                           ->  in the worst case, run OS commands on the server

Why it is consistently near the top of every risk list

A single injectable field can expose an entire database, and databases hold exactly the crown jewels attackers want. Decades on, SQL injection still causes major breaches, almost always because input was concatenated into a query somewhere. The vulnerability is old and famous, yet it survives because the unsafe way of building queries is often the quickest way, and quick habits are hard to kill.

Stopping it

Here is the good news, and it is genuinely good news. There is one real fix, it is not hard, and it defeats every flavour above at once. The rest are supporting layers that reduce the blast radius when something else goes wrong.

  1. Use parameterised queries, always. Also called prepared statements, these send the query and the data to the database as two separate things, never mixed into one lump of text. The application tells the database "here is the shape of the command, with a placeholder", and then, separately, "here is the value that goes in the placeholder". Because the value arrives on its own channel, the database treats it strictly as data. It can never be read as a command, no matter what punctuation it contains. This is the fix. Everything else is secondary.
Unsafe:  "... WHERE username = '" + input + "'"
Safe:    "... WHERE username = ?"   with input supplied as a bound parameter

In the safe version, the ? is a placeholder. Whatever the attacker types, even admin' --, is handed over as a plain value that fills that one slot. The clever quote and the comment become just an odd looking username that matches no one. The attack quietly fails.

  1. Prefer a well used ORM or query builder. An ORM (object relational mapper) is a library that lets you work with database records as normal objects in your code, and it writes the SQL for you. These tools parameterise by default, so as long as you do not deliberately drop back down to raw string concatenation, you are largely protected without thinking about it.
  2. Apply least privilege on the database account. Least privilege means giving the account only the access it genuinely needs and nothing more. If the part of your app that shows product listings connects with an account that can only read, then even a successful injection there cannot delete tables or touch the users. It shrinks the damage.
  3. Validate input as a supporting measure, and use a web application firewall as defence in depth. Validation means checking that input looks the way it should (a postcode looks like a postcode), and a firewall can block obvious attack patterns before they arrive. Both are useful, but be clear eyed: neither replaces parameterised queries. They are the seatbelt, not the brakes.

Do not try to filter your way to safety

A tempting but broken approach is to strip out dangerous characters like single quotes, or to escape them by hand. Attackers have decades of tricks to slip past such filters, using different encodings, comment styles, or database quirks. Blocklisting bad input is a losing game. Parameterisation sidesteps it entirely by never letting input be interpreted as code in the first place.

The takeaway

SQL injection happens when user input is concatenated into a query and the database reads it as commands, which can expose or destroy everything it holds. It comes in in band, blind, and out of band forms, but they all share one root cause and one cure: never build queries by pasting in input, use parameterised queries so data stays data. Layer least privilege and input validation on top to limit the damage if anything slips, but treat those as supports, not the foundation. Keep that one line clean, data on its own channel, and this decades old threat simply cannot occur.

It is the database cousin of cross site scripting, where the very same "data became code" mistake plays out in the browser instead of the database. If you recognise the pattern in one, you will start spotting it everywhere.