SQL Injection Isn't Dead Yet
Tags: security, sql
Published: 2024-04-15 12:51:14

**by Erlend Oftendal and Naane Baars**

SQL injection was introduced in an [article by Rain Forrest Puppy (Jeff Forristal) in Phrack](http://phrack.org/issues/54/8.html) 25 years ago. Even though it is a well-known bug with a well-known remedy, it still frequently occurs even in today's products.

![XKCD: Explits of a Mom](https://imgs.xkcd.com/comics/exploits_of_a_mom.png)

If we look at the [OWASP Top 10](https://owasp.org/Top10/), injection risk started in the 6th position in the initial 2003 version, and then moved across the top three spots in the later versions.

![A timeline of SQL injection's rank in the OWASP Top 10](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gji4xdhvqssj2w8q6bre.png)

SQL injection is also number 3 on the [SANS CWE Top 25 Most Dangerous Software Errors](https://www.sans.org/top25-software-errors/), and is still frequently on the reports from penetration tests and bug bounty programs, although there is a declining trend. If we look at [CVE details of 2023](https://www.cvedetails.com/vulnerability-list/year-2023/opsqli-1/sql-injection.html) we get a list of 2159 of 29065 vulnerabilities found.

CISA, together with the FBI, recently released a Design Alert called [Eliminating SQL Injection Vulnerabilities in Software](https://www.cisa.gov/resources-tools/resources/secure-design-alert-eliminating-sql-injection-vulnerabilities-software), asking for all call to action to remediate this vulnerability once and for all.

That begs the question: why, after all these years, does SQL injection still crop up? It should be a thing of the past.

## What Is an SQL Injection?

SQL injection typically occurs where attacker-controlled data is concatenated with strings to build SQL queries. The classic example is logins where the application attempts to look up a username and password in the database:

```python
query = "SELECT * FROM users WHERE username='" + username + "' AND password='" + password "'"
result = db.execute(query)
```

This type of query is not very common in modern applications for various reasons. Passwords are not stored verbatim, but hashed with a fitting hashing algorithm as per the [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html). Additionally, this specific type of bug is often quickly found in penetration tests and scans. As a result, developers might falsely assume that they have avoided the risk of SQL injection.

In practice, SQL injection can take many forms and attacker-controlled data can come from many different locations in the code. We typically think of attacker-controlled data as form inputs, parameters from the URL, or data from a JSON body of an HTTP request, but input can come from all parts of an incoming request. This includes header values and encoded values in tokens such as JWT, embedded in image EXIF data, or even in encrypted data, among many other forms. There are even examples of SQL injection in door entry systems where the input is coming from proprietary wire protocols.

The most common approach to avoiding SQL injection is the use of parameterized queries (or prepared statements), where the data inputs are replaced with placeholders and separated from the query itself:

```sql
select * from users where username = :username AND password = :password
```

With this an attacker is no longer able to change the meaning of the query. However, there are cases where using parameterized queries alone is not enough. Suppose we have an application where the user is allowed to change the sort order of the data. The query could look something like:

```sql
select * from servers order by hostname
```  

In the code that is generating this statement, the `order_by` column may be added by the code like this.

```python
def fetchServers(order_by: str):
  db.execute("select * from servers order by " + orderBy)
  ...
```

One thing to note is that you cannot use a parameterized query for the `order by` clause. The order by clause will normally be a column name, however if we look at the SQL grammar definition it can be a complete expression. This would also be a valid statement:

```sql
select * from servers
order by case when (
    select ip from servers
    where substr(ip,1,1) = '9'
) IS NOT NULL
then hostname
else id end
```

The attacker can look at the ordering of the results and tell you whether the query sent to the database is matching something. Again we end up with a SQL injection. This manual process can be easily accelerated with SQLMap, for example. To fix this you need to validate the column passed to our function above, preferably against a strict allow-list.

In short, many SQL injection risks can be avoided with a combination of parameterized queries and validation. For all possible mitigations have a look at the [OWASP SQL Injection Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html).

## Remain Vigilant

One reason SQL injections remain a threat is a persistent lack of awareness and due diligence. Many developers use platforms like StackOverflow and tools like GitHub Copilot to quickly find answers, but end up with code vulnerable to SQL injection. A simple copy-and-paste or autocomplete, and you introduce that vulnerability inside your own code. Even if someone in the answers section on StackOverflow points out the SQL injection, it often isn't the top answer or comment.

Code scanners (SAST) and application scanners (DAST) can certainly find many of the SQL injection issues, but may miss some due to lack of framework support or because they don’t scan all the possible injection points. Maybe AI will one day be able to flawlessly detect SQL injections, and we will get rid of this threat forever. In the meantime, we must remain vigilant.

## Misplaced Trust

Another source of SQL injection can be the libraries used to communicate with the database. Many developers assume that the libraries have done things right, but that trust is too often misplaced. There are cases where even if you as a developer have done everything right in your code, the application is still vulnerable to SQL injection.

One such example is the recent [CVE-2024-1597](https://www.cve.org/CVERecord?id=CVE-2024-1597) in the Java postgresql driver (although this had some really specific preconditions). Another example is [CVE-2019-14900](https://www.cve.org/CVERecord?id=CVE-2019-14900) which was a flaw in Hibernate ORM.

WordPress plugins have also been a running source of SQL injection. Some examples from 2023 alone include [CVE-2023-23488](https://www.cve.org/CVERecord?id=CVE-2023-23488), [CVE-2023-23489](https://www.cve.org/CVERecord?id=CVE-2023-23489), [CVE-2023-23490](https://www.cve.org/CVERecord?id=CVE-2023-23490), [CVE-2023-26325](https://www.cve.org/CVERecord?id=CVE-2023-26325), [CVE-2023-28659](https://www.cve.org/CVERecord?id=CVE-2023-28659) and [CVE-2023-28660](https://www.cve.org/CVERecord?id=CVE-2023-28660) released last year. This year, [CVE-2024-1071](https://www.cve.org/CVERecord?id=CVE-2024-1071) was published.

To detect these types of vulnerabilities, we should first and foremost know our dependencies and versions, and which of them have vulnerabilities. The OWASP Top 10 2021 identifies this need as [A06:2021-Vulnerable and Outdated Components](https://owasp.org/Top10/A06_2021-Vulnerable_and_Outdated_Components/). OWASP has several tools for this, including  [Dependency Check](https://owasp.org/www-project-dependency-check/) and [Dependency Track](https://dependencytrack.org/). These tools will warn about the use of components with vulnerabilities.

If the SQL injection vulnerabilities are not known, there are chances they can be detected by scanning the code, scanning the applications, or running a manual penetration test. Although these are not guaranteed to find the bugs, there is an increased chance of finding the vulnerabilities before attackers do.

## Learn More About SQL Injection

It is essential that developers learn to spot, prevent, and fix SQL injections. OWASP has several resources to enable you to do just that, and have some fun in the process!

[WebGoat](https://owasp.org/www-project-webgoat/) and [Juice Shop](https://owasp.org/www-project-juice-shop/) are two "deliberately insecure" applications containing hundreds of security vulnerabilities for you to find and exploit, including SQL injections. Both projects provide extensive educational material to guide you.

The [SQL Injection Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html) is an indispensible reference for defending against SQL injection in your own project. You can also find some more interesting examples on the [OWASP community page for SQL injection](https://owasp.org/www-community/attacks/SQL_Injection).

Remember: only _you_ can prevent SQL injection!

---

[OWASP](https://owasp.org) is a non-profit foundation that envisions a world with no more insecure software. Our mission is to be the global open community that powers secure software through education, tools, and collaboration. We maintain hundreds of open source projects, run industry-leading educational and training conferences, and meet through over 250 chapters worldwide.