Now that you understand the OWASP Top 10 landscape, we dive into the first major vulnerability category: Injection. SQL Injection has been a critical threat for over two decades and continues to cause massive data breaches. In this lesson, you will learn how it works, why it happens, and how to exploit it in your lab environment.
SQL Injection occurs when an attacker inserts malicious SQL code into a query that the application sends to its database. If the application does not properly validate or sanitize user input, the database executes the attacker's SQL code as if it were part of the original query.
The consequences can be severe: reading sensitive data from the database, modifying or deleting data, executing administrative operations on the database, and in some cases, executing operating system commands on the server itself.
Consider a simple login form. The application takes the username and password you enter and constructs a SQL query to check if they match a record in the database. Here is what vulnerable code looks like:
<?php
// VULNERABLE CODE - DO NOT USE IN PRODUCTION
$username = $_POST['username'];
$password = $_POST['password'];
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = mysqli_query($connection, $query);
if (mysqli_num_rows($result) > 0) {
echo "Login successful!";
} else {
echo "Invalid credentials.";
}
?>When you enter a normal username like 'admin', the query becomes: SELECT * FROM users WHERE username = 'admin' AND password = 'secret123'. This is exactly what the developer intended. But what happens when an attacker enters something unexpected?
-- Attacker enters as username: admin' --
-- The resulting query becomes:
SELECT * FROM users WHERE username = 'admin' --' AND password = ''
-- Everything after -- is a comment, so the database ignores the password check!
-- The attacker logs in as admin without knowing the password.⚠️ Practice this only in your lab environment (DVWA, WebGoat). Attempting SQL Injection against real applications without authorization is illegal and unethical.
Let's practice with DVWA's SQL Injection module. Set the security level to 'Low' in DVWA's settings. Navigate to the 'SQL Injection' page. You will see a form that asks for a User ID.
Enter a normal value like '1' and click Submit. The application returns the user's first name and surname. Now let's see what happens when we inject SQL.
By injecting ' OR '1'='1, we changed the query's logic. Instead of returning only one user, the database returns every row in the users table because '1'='1' is always true. This is the classic SQL Injection attack.
DVWA's low-security SQL Injection page runs a query similar to this:
-- Original intended query:
SELECT first_name, last_name FROM users WHERE user_id = '1'
-- After injection with 1' OR '1'='1:
SELECT first_name, last_name FROM users WHERE user_id = '1' OR '1'='1'
-- The OR '1'='1' condition makes every row match💡 The key insight: SQL Injection works because the application concatenates user input directly into the SQL string. The database cannot distinguish between the developer's intended SQL and the attacker's injected SQL — it all looks like one query.
The UNION operator combines the results of two SELECT statements. Attackers use UNION SELECT to extract data from other tables in the database. But UNION requires both SELECT statements to have the same number of columns.
First, we need to determine how many columns the original query returns. We do this by incrementally adding UNION SELECT with NULL values until the query succeeds.
Now that we know there are 2 columns, we can extract data. Let's get the database version and the current database user.
We now know the database version and user. This information helps us understand the database structure and plan further extraction. Let's enumerate the tables.
The information_schema database is a metadata database that contains information about all other databases, tables, and columns. It is the attacker's best friend during SQL Injection attacks — it tells you exactly what data is available to steal.
Now that we know there is a 'users' table, let's find its columns and extract the data.
The passwords are MD5 hashes. The hash '5f4dcc3b5aa765d61d8327deb882cf99' is the MD5 hash of 'password' — one of the most commonly used passwords. You can crack these hashes using online tools like CrackStation or hashcat in your lab.
SQL Injection exists because of a single root cause: the application treats user input as trusted code. When user data is concatenated directly into SQL queries, the boundary between data and code is destroyed.
The proper defense against SQL Injection is parameterized queries (also called prepared statements). With parameterized queries, the SQL structure is defined first, and user input is passed as parameters — never interpreted as SQL code.
<?php
// SECURE CODE - Using Prepared Statements
$stmt = $connection->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo "Login successful!";
} else {
echo "Invalid credentials.";
}
?>With prepared statements, even if an attacker enters admin' -- as the username, the database treats the entire string as a literal username value — it searches for a user whose username is literally "admin' --". No SQL code is ever executed from user input.
💡 Parameterized queries are the gold standard defense. They work in every programming language and every major database. There is almost no reason to use string concatenation for SQL queries in modern development.
Verify exercises to earn ★ 150 XP and unlock next lab level.