Open In App

SQL Injection

Last Updated : 04 Sep, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

SQL Injection is a security flaw in web applications where attackers insert harmful SQL code through user input. This can allow them to access sensitive data, change database contents, or even take control of the system. It's important to know about SQL Injection to keep web applications secure.

SQL Injection (SQLi) is a security vulnerability that occurs when an attacker can manipulate a web application's database queries by inserting malicious SQL code into user input fields. These injected queries can manipulate the underlying database to retrieve, modify, or delete sensitive data. In some cases, attackers can even escalate privileges, gaining full control over the database or server.

sql injection

Real-world Example:

In 2019, the Capital One Data Breach occurred due to a misconfigured web application that allowed an attacker to exploit a SQL injection vulnerability. This resulted in the leak of personal data of over 100 million customers, including names, addresses, and credit scores.

SQL Injection Security Level

DVWA provides four security levels for SQL Injection to help learners see how different protections affect attacks:

1. Low Security

The app takes your input and directly puts it into the SQL query with no filtering.

$id = $_GET['id'];$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
  • Entering ': Breaks the query and makes the database throw an error, revealing it is vulnerable.
  • Entering 1' OR '1'='1: Tricks the query into always being true, so all users are returned.
  • Entering 1' UNION SELECT user, password FROM users--: Joins another query to fetch hidden data like usernames and passwords.

2. Medium Security

The app applies basic input sanitization using functions like addslashes() to escape '.

$id = addslashes($_GET['id']);$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

How can be Attack:

A simple ' injection won’t work anymore (because it becomes \').

But attackers can still bypass using numeric injection (since numbers don’t need quotes).
Example:

1 OR 1=1

This still returns all records.

3. High Security

The app uses prepared statements (parameterized queries) to safely handle user input.

$stmt = $pdo->prepare("SELECT first_name, last_name FROM users WHERE user_id = ?");$stmt->execute([$id]);

Attack:

Attempts like ', OR 1=1, or UNION SELECT no longer work.

The query treats all input as data, not SQL code.

Types of SQL Injection

There are different types of SQL Injection

1. Error-Based SQL Injection

Error-based SQL injection is a type of in-band SQL injection where an attacker intentionally causes the database to generate an error message. The attacker then analyzes this error message to gain valuable information about the database's structure, like table names and column names, which can be used to craft further, more precise attacks.

How It Works

This attack targets applications that reveal raw database errors instead of showing generic messages. By injecting malicious input that breaks the SQL syntax, attackers trigger these errors and gain valuable clues about the database structure.

  1. Identify a Vulnerable Input: The attacker finds an input field, like a search bar or a URL parameter, that directly interacts with the database without proper input sanitization.
  2. Inject a Malicious Payload: The attacker injects a special character (like a single quote ') or a function that is known to cause a database error.
  3. Analyze the Error: The database, unable to process the malformed query, returns a detailed error message. This message can reveal crucial information such as:
    • The database system (e.g., MySQL, Oracle, SQL Server).
    • The version of the database.
    • The full SQL query being executed.
    • Specific syntax errors that can be used to understand table or column names.
  4. Refine the Attack: Using the information gathered from the error message, the attacker can refine their payload to extract more data, such as usernames and passwords.

Example:

Step 1: Set Up Your Environment

  • Launch DVWA. It's typically accessed by navigating to a URL like http://localhost/dvwa in your browser.
file
  • Log in to DVWA with the default credentials: admin / password.
file
  • Go to the DVWA Security tab and set the security level to low. This will ensure the vulnerabilities are easy to exploit.
file

Step 2: Identify the Vulnerability

The SQL Injection page has a simple input box where you can enter a user ID. The backend query is likely something like SELECT * FROM users WHERE id = 'user_input'

  • Enter a valid ID, like 1, into the input box and click "Submit." You should see the details for the user with ID 1.
file

SQL Injection Source

PHP
<?php

    $id = $_REQUEST[ 'id' ];

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            // Check database
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

            // Get results
            while( $row = mysqli_fetch_assoc( $result ) ) {
                // Get values
                $first = $row["first_name"];
                $last  = $row["last_name"];

                // Feedback for end user
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }

            mysqli_close($GLOBALS["___mysqli_ston"]);
            break;
        case SQLITE:
            global $sqlite_db_connection;

            #$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']);
            #$sqlite_db_connection->enableExceptions(true);

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            #print $query;
            try {
                $results = $sqlite_db_connection->query($query);
            } catch (Exception $e) {
                echo 'Caught exception: ' . $e->getMessage();
                exit();
            }

            if ($results) {
                while ($row = $results->fetchArray()) {
                    // Get values
                    $first = $row["first_name"];
                    $last  = $row["last_name"];

                    // Feedback for end user
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
            } else {
                echo "Error in fetch ".$sqlite_db->lastErrorMsg();
            }
            break;
    } 
}
ode
?>
  • Now, try to break the query. Enter a single quote ' in the input box and submit.
file

The query becomes:

SELECT * FROM users WHERE id = ''';

Here the database sees an extra quote and doesn’t know how to complete the query.

Instead of showing you the user details, the application will return an SQL error (something like "You have an error in your SQL syntax…")

This is called Error-Based SQL Injection because:

  • The attacker sends invalid input (')
  • The database throws an error
  • That error leaks useful information about the database (like type of DB, number of columns, structure, etc.)

2. Union-Based SQL Injection

Union-based SQL Injection is a technique where attackers use the UNION operator to combine the results of two or more SELECT statements into a single result set. This can allow them to extract information from other tables in the database. The UNION operator can only be used if:

  • Both queries have the same number of columns
  • The columns have similar data types
  • The columns are in the same order

UNION Operator: The UNION operator is used to combine the result-set of two or more SELECT statements.

  • Each SELECT statement within UNION must have the same number of columns
  • The columns must have similar data types
  • The columns must be in the same order
SELECT column_name(s) FROM table1UNIONSELECT column_name(s) FROM table2

Example:

Step 1: Firstly we have to find the number of columns of the existing table in the website to inject UNION based SQL Injection:

The SQL Injection page has a simple input box where you can enter a user ID. The backend query is likely something like

 SELECT * FROM users WHERE id = 'user_input'

Now, try to break the query. Enter a single quote ' in the input box and submit.

If the application is vulnerable, you will get a detailed error message. It might look something like:

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1

Step 2: Use the UNION Keyword to Discover the Number of Columns

To use the UNION keyword (a common next step), you need to know the number of columns in the original query. You can find this out by using the ORDER BY clause

  • Try to sort the results by column
1: 1 ORDER BY 1. 
  • Submit. It should work.
file

SQL Injection Source

PHP
<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
    // Get input
    $id = $_REQUEST[ 'id' ];

    switch ($_DVWA['SQLI_DB']) {
        case MYSQL:
            // Check database
            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

            // Get results
            while( $row = mysqli_fetch_assoc( $result ) ) {
                // Get values
                $first = $row["first_name"];
                $last  = $row["last_name"];

                // Feedback for end user
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }

            mysqli_close($GLOBALS["___mysqli_ston"]);
            break;
        case SQLITE:
            global $sqlite_db_connection;

            #$sqlite_db_connection = new SQLite3($_DVWA['SQLITE_DB']);
            #$sqlite_db_connection->enableExceptions(true);

            $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
            #print $query;
            try {
                $results = $sqlite_db_connection->query($query);
            } catch (Exception $e) {
                echo 'Caught exception: ' . $e->getMessage();
                exit();
            }

            if ($results) {
                while ($row = $results->fetchArray()) {
                    // Get values
                    $first = $row["first_name"];
                    $last  = $row["last_name"];

                    // Feedback for end user
                    echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
                }
            } else {
                echo "Error in fetch ".$sqlite_db->lastErrorMsg();
            }
            break;
    } 
}
?>
  • Increment the number:
 1 ORDER BY 2. 

Submit. It should work.

file
  • Keep incrementing until you get an error. For example, 1 ORDER BY 4 might give you: Unknown column '4' in 'order clause'
  • This means the query has 3 columns.

3. Blind-Based SQL Injection

Blind SQL Injection occurs when attackers cannot see query results directly on the webpage. Instead, they infer information from subtle changes in the application’s behavior or response time. Although slower and more tedious than classic SQLi, it can be equally effective.

Instead of getting data back, the attacker infers information by observing the web page's behavior. This is typically done in one of two ways:

  1. Boolean-Based Blind SQLi: The attacker injects a SQL query that returns a true or false result. The web application's response changes based on whether the query is true or false. For example, the page might show a different message or render a different layout.
  2. Time-Based Blind SQLi: The attacker injects a SQL query that causes the database to perform a time-consuming action (like a SLEEP() function) if a condition is met. The attacker observes the time it takes for the page to load to determine if the injected condition was true or false.

Example:

Imagine a login page where you enter a username and password. The application constructs a SQL query like this:

SELECT * FROM users WHERE username = 'user_input' AND password = 'password_input'

A blind SQL injection would involve manipulating the user_input field to ask the database a question.

Instead of getting a direct response, the attacker might try something like this:

user_input = 'admin' AND 1=1; --

If the page loads normally, the attacker knows that 1=1 is a true statement.

user_input = 'admin' AND 1=2; --

If the page shows an error or behaves differently, the attacker knows that 1=2 is a false statement.

file

By using a series of these true/false questions, an attacker can systematically guess and extract information, one character at a time. The process can be automated to guess everything from table names to user passwords.

Impact of SQL Injection Attacks

  • Unauthorized access to sensitive data: Attackers can retrieve personal, financial, or confidential information stored in the database.
  • Data integrity issues: Attackers can modify, delete, or corrupt critical data, impacting the application's functionality.
  • Privilege escalation: Attackers can bypass authentication mechanisms and gain administrative privileges.
  • Service downtime: SQL injection can overload the server, causing performance degradation or system crashes.
  • Reputation damage: A successful attack can severely harm the reputation of an organization, leading to a loss of customer trust.

Preventing SQL Injection Attacks

There are several best practices to prevent SQL injection attacks:

1. Use Prepared Statements and Parameterized Queries

Prepared statements and parameterized queries ensure that user inputs are treated as data rather than part of the SQL query. This approach eliminates the risk of SQL injection.

Example in PHP (using MySQLi):

$stmt = $conn->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();

2. Employ Stored Procedures

Stored procedures are predefined SQL queries stored in the database. These procedures can help prevent SQL injection because they don't dynamically construct SQL queries.

Example:

CREATE PROCEDURE GetUserByUsername (IN username VARCHAR(50))
BEGIN
SELECT * FROM users WHERE username = username;
END;

3. Whitelist Input Validation

Ensure that user inputs are validated before being used in SQL queries. Only allow certain characters and patterns, such as alphanumeric input, for fields like usernames or email addresses.

4. Use ORM Frameworks

Object-Relational Mapping (ORM) frameworks like Hibernate or Entity Framework can help prevent SQL injection by automatically handling query generation, preventing dynamic query construction.

5. Restrict Database Privileges

Grant the minimum required database permissions to users. Ensure that applications can only perform necessary actions (e.g., SELECT, INSERT), and restrict permissions like DROP TABLE or ALTER.

6. Error Handling

Configure the database and application to not display detailed error messages to the user. Instead, log errors internally and display generic error messages to end users.


SQL Injection
Article Tags :