Rust SQL Injection Guide:
Examples and Prevention

stackhawk

StackHawk|July 30, 2021

This guide covers SQL injections, a few examples, and a walkthrough of how to fix and prevent them specific to Rust-Lang coding.

Very few languages have sparked confidence and certainty for security-critical applications at the level that Rust-Lang has. (Here's a Rust worship wagon for reference.) Even with a peace-of-mind-inducing statement like this, there's always a key loophole that hackers can take advantage of — you! To be exact, the fact that applications often need to interact with users through input makes them susceptible to Rust SQL injection. 

This post explores just how vulnerable Rust applications are to SQL injections. We'll give a couple of examples, along with methods, crates, and coding etiquette that seals out Rust SQL injection efforts. 

Before we start running cargo commands on crates, let's take a look at the concept of SQL injection. At least enough to fully grasp the problem and come up with preemptive solutions thereof. 

Rust SQL Injection Guide: Examples and Prevention image

What Is an SQL Injection?

Just as you furnish forms fields with required data, hackers can also input structured query language (SQL) commands into applications' databases through the same input controls. This forced access technique is popularly known as SQL injection. 

The exploit thrives on this premise: 

Where you'd respond to a variable character (varchar) field with guided input, a string that the DBMS parses as a command is pushed in. Once executed, the injection prompts a targeted database to comply — which it does because it was originally built to accept SQL commands.

Safe to say, developers whose applications are attacked this way seldom expect vulnerabilities in their apps. This is a predicament that often ends with costly and embarrassing results.

SQL injections are very popular. They account for well over half of all discovered hacks. You'd think every web application implements some sanitization before passing queries to the database. The fact that new attacks come to the surface on the regular suggests otherwise.

When creating apps with Rust-Lang SQL, you can use any of these popular RDBMSs: 

  • Microsoft SQL Server

  • MySQL

  • Oracle

  • MariaDB

  • Postgres

They all bring along that SQL injection problem along, so it makes sense to develop from a knowing angle. Let's take a look at a few Rust SQL injection scenarios before unloading solutions.

Rust SQL Injection Examples

The first thing a hacker does when preparing a full-on SQL injection attack is to get the names of tables behind an application. To create a map of tables and columns, they'll spend time intentionally promoting your database to produce error messages. These messages often expose enough details about a database for the reader to resolve them. That same knowledge comes in handy when learning the schema bit by bit. 

A popular injection that accomplishes this outcome is the out-of-place single quote ( ' ).  Instead of a non-spaced string, the attacker inserts this input: 

Name: Some 'name

The field will process the name as an expected variable, but the database field won't accept it. If you're using bare SQL connections and scripts, this query will look like this:

INSERT INTO [your table name] ([column value]) VALUES ("Some 'name");

With luck, the obvious "Users," "products," and maybe "customers" tables they can run queries against will have different names in your database. The command above results in an error that, depending on the DBMS, exposes where to fix in detail. 

mysql: could not prepare statement .... 

Once a hacker knows which specific DBMS, DB, and tables contain elements of interest, the next step would be to extract or alter them. Unsanitized, your connections to an SQL database take raw input variables and pass them forward. This is done by causing the DBMS to confuse input for commands. 

Typically, 1 = 1 always translates to a Boolean truth. So a query that includes: 

OR "1 = 1",

will execute the upcoming command. This opens your application to a plethora of attacks, including a complete DROP. 

Just these two scenarios are enough to prove the need to sanitize Rust applications from SQL injection attacks. Let's explore the best ways to achieve this.

Rust SQL Injection Guide: Examples and Prevention image

How to Fix Rust SQL Injection Vulnerabilities

Application security is best practiced when each line of code in its makeup escapes the developer's mind. You either agree on how every text field entry is processed or serve the required sanitation through crates. The goal is to check database connection queries and any loaded variables for SQL injections at runtime.

Crates That Protect Against Rust SQL Injection

The crates route is easier than manually including custom type screening functions for each user interaction. Most crates implement object-relational mapping (ORM) to sheath SQL commands with Rust-Lang. This way, no scripts pass from the front end. Let's explore this in detail. 

1. The libinjection crate: Use this crate to bind Rust SQL injection snippets to a discovery method. This typically uses a fingerprints.txt tile to map input from uses into a vulnerability alert condition—at which point, such input is denied. 

2. SQLx: This is a crate filled with Rust-coded checks for SQL input. You can use it with MSSQL, MySQL, Postgres, and SQLite. It's not an ORM, which means you'll still need to connect and transact with databases through naked SQL queries. 

If you're still able to see the code, this means hackers can keep trying to pass commands in different means than those passing through the crate. This is where an ORM beats the simple crates route to Rust SQL injection attacks. 

3. Diesel: This is an ORM that turns bare SQL lines of code into CRUD statements purely comprised of Rust logic. Let's look at the sample code we used in our example:

INSERT INTO [your table name] ([column value]) VALUES ("Some 'name");

turns into:

#[derive(Insertable)]
#[table="table name here"]
struct NewItem<'a> {
column1: &'a str,
column2: Option<&'a str>,
}
let new_item = vec![
NewItem { column1: "value1", column2: "value2" },
NewItem { name: "value3", column2: "value4" },
];
insert_into(table)
.values(&new_item)
.execute(&connection);

You should already be noticing how many details of the actual database are hidden. For one, the DB connection string is nowhere to be found. This is because Diesel requires a URL to the database in a separate environment file. If this doesn't discourage injection reconnaissance, it should at least delay attacks enough for your APMs to pick up unusual error activity.

Custom Rust SQL Injection Sanitization

If you were sure that creating your own library or function is the way to go, the thinking process would go something like this: 

  1. Accept data from fields on the front end of your Rust application.

  2. Validate all entries through a preset array of allowable characters (a good way of removing special characters).

  3. Make certain that all entry is of policed length.

  4. Convert all entries into string format and assign them to new private custom data types.

  5. Push variables to the database for processing. You can also have a pre-processor in between to filter known commands from being executed by the database.

This process will take more time to compile. Thus, your applications will be less enjoyable compared with those that go the crates route.

The Takeaway

Rust SQL injections pivot on the fact that databases compile queries predictably. This allows vulnerable applications susceptible to a range of operations from updates, copy, and even erase commands. With that said, you're better off adding crates to sanitize your Rust applications from potential SQL injection attempts.

Making sure attackers don't exploit any vulnerabilities is a continuous process.

This is especially true with the CI/CD approach to versioning, which involves multiple developers adding raw code to your project. New code means new possible vulnerabilities. For this reason, an omniscient code scanner and test tool such as StackHawk comes in handy, enforcing a security-first programming process. 

This post was written by Taurai Mutimutema. Taurai is a systems analyst with a knack for writing, which was probably sparked by the need to document technical processes during code and implementation sessions. He enjoys learning new technology and talks about tech even more than he writes.


StackHawk  |  July 30, 2021