Lab 09 β Web Vulnerabilities: SQL Injection¶
Course: SCIA-120 Β· Introduction to Secure Computing
Topic: Secure Programming β OWASP Top 10 / SQL Injection
Difficulty: ββ BeginnerβIntermediate
Estimated Time: 60β75 minutes
Related Reading: Chapter 8 β Internet Security, Chapter 9 β Secure Programming
Overview¶
SQL Injection (SQLi) is consistently ranked #1 in the OWASP Top 10 most critical web application vulnerabilities. It occurs when user input is directly inserted into a database query without sanitization, allowing attackers to manipulate the query logic. In this lab you will run a deliberately vulnerable web application in Docker, exploit it to extract data, and then apply the fix.
Educational Purpose
This lab uses an intentionally vulnerable application designed for security education. All attacks are performed against a local Docker container you control. Never attempt these techniques on systems you do not own or have explicit written permission to test.
Learning Objectives¶
- Understand why SQL injection occurs (unsanitized user input in queries).
- Perform a basic SQL injection to bypass a login.
- Extract database contents using UNION-based injection.
- Apply parameterized queries to fix the vulnerability.
- Understand the difference between vulnerable and secure code.
Prerequisites¶
- Docker Desktop installed and running.
Part 1 β Launch the Vulnerable Application¶
We will use DVWA (Damn Vulnerable Web Application) β an intentionally insecure app built for education.
Step 1.1 β Start DVWA¶
Step 1.2 β Wait and Verify¶
Step 1.3 β Initialize the Database¶
Open a browser and go to: http://localhost:8888
- Log in with:
admin/password - Scroll to the bottom and click "Create / Reset Database"
- Log in again with the same credentials
πΈ Screenshot checkpoint: Take a screenshot of the DVWA dashboard after logging in.
Step 1.4 β Set Security Level to Low¶
In the DVWA menu, click "DVWA Security" β set to Low β Submit.
πΈ Screenshot checkpoint: Take a screenshot showing security level set to "Low".
Part 2 β Understanding the Vulnerability¶
Navigate to SQL Injection in the left sidebar.
You'll see a form that says: "User ID: [ input ] [ Submit ]"
The backend PHP code for this form looks something like:
// VULNERABLE CODE β DO NOT USE IN REAL APPLICATIONS
$id = $_GET['id'];
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
The problem: $id is inserted directly into the SQL query without any sanitization.
Step 2.1 β Normal Use¶
Enter 1 in the User ID field and submit.
Result: You see admin β the user with ID 1.
πΈ Screenshot checkpoint: Take a screenshot of the normal query result.
Step 2.2 β Test for SQL Injection¶
Enter 1' (with a single quote) and submit.
Expected result: A database error β because the single quote broke the SQL query syntax. This confirms the input field is injectable.
πΈ Screenshot checkpoint: Take a screenshot of the SQL error message.
Part 3 β Exploiting SQL Injection¶
Step 3.1 β Boolean-Based: Always-True Condition¶
Enter this in the User ID field:
What this does: The query becomes:
Since '1'='1' is always true, this returns all users in the database.
πΈ Screenshot checkpoint: Take a screenshot showing all users returned.
Step 3.2 β Login Bypass (Classic Attack)¶
Navigate to the DVWA login page and try logging in with: - Username: admin' -- - Password: anything
The -- comments out the rest of the SQL query, including the password check:
πΈ Screenshot checkpoint: Take a screenshot showing the login bypass result.
Step 3.3 β Extract Database Information with UNION¶
Enter this in the SQL Injection form:
What this does: Appends a second SELECT to the query that returns the current database user and database name.
πΈ Screenshot checkpoint: Take a screenshot showing the database user and database name revealed.
Step 3.4 β Extract Table Names¶
1' UNION SELECT table_name, table_schema FROM information_schema.tables WHERE table_schema=database() --
πΈ Screenshot checkpoint: Take a screenshot showing the database table names extracted.
Part 4 β The Fix: Parameterized Queries¶
Step 4.1 β Understand the Root Cause¶
The vulnerability exists because user input is concatenated directly into the SQL string. The fix is to use parameterized queries (also called prepared statements) β the database treats input as pure data, never as SQL code.
Vulnerable code (PHP):
// DANGEROUS
$id = $_GET['id'];
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysqli_query($conn, $query);
Secure code (PHP with parameterized query):
// SAFE
$id = $_GET['id'];
$stmt = $conn->prepare("SELECT first_name, last_name FROM users WHERE user_id = ?");
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result();
Step 4.2 β See the Fix in Action in DVWA¶
Change DVWA security to Medium (DVWA Security β Medium β Submit).
Navigate to SQL Injection and try the same injection: 1' OR '1'='1
Observe: The Medium security level escapes special characters. The injection no longer works as expected.
Navigate to SQL Injection (Blind) β this page uses prepared statements. Try the same injection β it simply returns no result or the expected single result.
πΈ Screenshot checkpoint: Take a screenshot showing the injection no longer works on Medium/High security.
Step 4.3 β Python Demonstration of Parameterized Queries¶
docker run --rm python:3.11-slim bash -c "
python3 -c \"
import sqlite3
conn = sqlite3.connect(':memory:')
conn.execute('CREATE TABLE users (id INT, name TEXT, password TEXT)')
conn.execute(\\\"INSERT INTO users VALUES (1, 'alice', 'secret')\\\")
conn.execute(\\\"INSERT INTO users VALUES (2, 'bob', 'hunter2')\\\")
# Vulnerable approach
user_input = \\\"1' OR '1'='1\\\"
print('=== VULNERABLE (string concat) ===')
try:
cur = conn.execute(f\\\"SELECT name FROM users WHERE id = '{user_input}'\\\")
print('Results:', [r[0] for r in cur.fetchall()])
except Exception as e:
print('Error:', e)
# Safe approach (parameterized)
print('=== SAFE (parameterized) ===')
cur = conn.execute('SELECT name FROM users WHERE id = ?', (user_input,))
print('Results:', [r[0] for r in cur.fetchall()])
print('(No results β injection attempt neutralized)')
\"
"
πΈ Screenshot checkpoint: Take a screenshot showing the vulnerable vs. safe query behavior.
Cleanup¶
Lab Assessment¶
Screenshot Submission Checklist¶
- [ ]
screenshot-09aβ DVWA dashboard logged in - [ ]
screenshot-09bβ Security level set to "Low" - [ ]
screenshot-09cβ Normal query result (User ID = 1) - [ ]
screenshot-09dβ SQL error from single quote input - [ ]
screenshot-09eβ All users returned with OR injection - [ ]
screenshot-09fβ Login bypass - [ ]
screenshot-09gβ UNION injection revealing DB user and name - [ ]
screenshot-09hβ Table names extracted - [ ]
screenshot-09iβ Injection blocked at Medium security - [ ]
screenshot-09jβ Python vulnerable vs. parameterized comparison
Reflection Questions¶
- Explain in your own words why SQL injection works. What is the fundamental programming mistake that makes it possible?
- What is a parameterized query, and how does it prevent SQL injection at a technical level?
- A company stores 10 million user records in a database. If SQL injection is exploited to dump the database, what types of damage could result? Consider financial, legal, and reputational impacts.
- Beyond parameterized queries, name two additional defenses against SQL injection. (Hint: think about input validation and database user privileges.)
Grading Rubric
- Screenshots complete and clearly labeled: 40 points
- Vulnerable vs. secure code explanation: 20 points
- Reflection questions answered thoughtfully: 40 points
- Total: 100 points