Golang SQL Injection Guide:
Examples and Prevention

stackhawk

StackHawk|August 5, 2021

No language is immune to security issues, and Go isn't an exception. In this post, learn about Golang SQL injections and how to prevent them.

It's not that hard to protect yourself from SQL injections. However, many people don't do it. That's why, year after year, SQL injections continue to rank as one of the most common and damaging security threats out there. No language is really immune to SQL injections. Go, which is often called "Golang" to make it more searchable, is certainly no exception. Despite being a very popular language created by a giant tech company, Golang SQL injections are certainly a thing.

In this post, we'll offer you a guide to fight SQL injections in Go. We'll open the post with some fundamentals by explaining SQL injections in general terms. You'll understand what they are and how they work. Then we'll proceed to the specific case of Go SQL injections, showing what you can do to shield your app from them. Unsurprisingly, this post assumes you have at least basic familiarity both with the Go language and with SQL and databases.

Before wrapping up, we'll share some final thoughts related to SQL injections and security in general, including some tips on how to better protect your applications and improve your organization's security.

Let's dig in.

What Is a SQL Injection?

A SQL injection is a type of attack by which an unauthorized actor successfully injects some SQL code into an application. With the injected code, the malicious actor is able to manipulate and change the database queries the application sends to the underlying database.

A successful SQL injection can have devastating effects. It can allow the attacker to access unauthorized sensitive information or — even worse — include, edit, or delete information from the database.

Examples of a Go SQL Injection

Let's see what a simple example of a SQL injection can look like in Go. Consider the following code:

id := "'58'";
query := fmt.Sprintf("SELECT name, email FROM users WHERE ID = %s, id);

As you can see, the code above just assembles a query—later to be sent to a database—formatting a predefined string with a parameter containing an ID. More realistically, the ID would have been passed as an argument, but for our purposes, the example is good enough.

So what exactly is the problem here?

Well, first understand that, in a real application, the ID parameter would likely originate from some user input instead of being hardcoded. Now you just have to imagine an ill-intentioned user passing, instead of a number, something like this:

'58' OR 1 = 1;--'

With such a parameter, the resulting query would look like this:

SELECT name, email FROM users WHERE ID = '58' OR 1 = 1;--

Since 1 is always equal to 1, executing the query above would result in all the users being retrieved!

However, SQL injections aren't only used for retrieving data. They can do much worse. Consider the following example:

id := "'58';DROP TABLE users--";
query := fmt.Sprintf("SELECT name, email FROM users WHERE ID = %s", id);
fmt.Println(query);

In the case above, the resulting query will be this:

SELECT name, email FROM users WHERE ID = '58';DROP TABLE users--

Yep, that's right. The attacker successfully nuked our users table. If you want to play around with the parameter to experiment with the results, just go to the Go Playground.

So how can you protect your Go apps against this type of attack?

Protecting Yourself Against SQL Injections

Luckily, protecting your app against SQL injections isn't that hard if you know what to do. The first step is understanding and using parameterized queries.

Parameterized Queries to the Rescue

Parameterized queries are the universal answer to SQL injection attacks. By using a parameterized query, you create a statement that is guaranteed to escape things properly, removing the risk of injections.

That works because instead of assembling the query ourselves by concatenating strings, we pass the values as parameters and let the database engine itself figure out how to replace the parameters with values and execute the query.

Using Parameterized Queries in Go

A parametrized query looks much like a normal query. But instead of using string formatting or concatenation to assemble the query, you use a placeholder for the parameters. The query from the previous example could look like this, for instance:

"SELECT name, email FROM users WHERE ID = ?"

In this case, the ? character is the placeholder that will be sent to the database engine.

It's important to clarify that placeholders themselves are database specific. Here are the different placeholders by database provider:

Golang SQL Injection Guide: Examples and Prevention image

Using Parameterized Queries in Practice

Generally speaking, you need two things to get started with parameterized queries in Go.

First, you need to import the package database/sql into your code. Also, you need the appropriate driver for your database provider, such as the pq driver for PostgreSQL. Here's a list of all of the available database drivers.

This is a complete example of connecting to a PostgreSQL database and executing a SELECT using a parameterized query:

import (
"database/sql"

_ "github.com/lib/pq"
)

func main() {
    connStr := "user=pqgotest dbname=pqgotest sslmode=verify-full"
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        log.Fatal(err)
    }

    age := 21
    rows, err := db.Query("SELECT name FROM users WHERE age = $1", age)

}

Let's explain the code above in detail:

  • The code starts by importing both the database/sql package and the specific driver for PostgreSQL.

  • Then it declares the function main() and creates a string variable containing the connection string.

  • The code then tries to open a connection with the created connection string.

  • After that, we do some error handling, logging the error in case it exists.

  • Finally, we declare a variable and assign a number to it and use the variable as the parameter for a parametrized query.

  • Going further, we would probably handle the errors from the query — if any — and then do something useful with the results.

Go SQL Injections: Go Get Rid of Them!

Go is a very successful language. In fact, according to the 2020 Stack Overflow Developer Survey, it ranks as the fifth-most-loved programming language. Go's success brought it fame, fortune, and everything that goes with it. However, some not-so-nice things were included in the package. For instance, security issues, including SQL injections.

SQL injections, despite being a widespread security threat, aren't usually that advanced. The fact that they still occur so often simply shows that engineers aren't educating themselves on basic security principles.

What can you, as an engineer, do to prevent SQL injections and other types of security vulnerabilities?

  • Education. Pursue education on security threats. Leverage opportunities to educate your coworkers—for instance, during pair-programming or code review sessions. You can learn more about the definition of SQL injections and its different types in our dedicated post.

  • Techniques. Adopt techniques to reduce the likelihood of injections and other types of attacks (such as using parameterized queries) and also to reduce damage in case a breach does occur, e.g., applying the principle of least privilege.

  • Tooling. Even experienced engineers will make mistakes eventually. That's why it's vital to leverage tools that can automatically detect security threats and add them to your CI/CD pipeline.

To sum it up: Though a SQL injection can be a serious issue, you're not hopeless against it. By keeping yourself educated on general security principles and using the tried-and-true approaches to working with databases, you'll be fine.

Stay tuned to the StackHawk blog for more security-related content. Thanks for reading, and until next time.

This post was written by Carlos Schults. Carlos is a consultant and software engineer with experience in desktop, web, and mobile development. Though his primary language is C#, he has experience with a number of languages and platforms. His main interests include automated testing, version control, and code quality.


StackHawk  |  August 5, 2021