SQL Injection

ID

go.sql_injection

Severity

critical

Resource

Injection

Language

Go

Tags

CWE:89, NIST.SP.800-53, OWASP:2021:A3, PCI-DSS:6.5.1

Description

Improper neutralization of special elements in SQL Commands ('SQL Injection' aka 'SQL').

SQL Injection vulnerabilities are predominant in applications that incorporate user input directly into SQL queries without adequate validation or sanitization. This allows malicious users to inject SQL commands that can alter the functionality of the original query.

By launching an SQL injection attack, threat actors may leak sensitive data from the database, alter data for their profit, delete data for denial of service, extract passwords and other credentials for offline dictionary attacks, or gain access to other systems within the network exploiting trust relationships.

Rationale

As with most injection vulnerabilities, inserting external input into SQL code is common practice when dealing with dynamic queries, as the engine may not provide options to code a table or column name(s), values for the IN operator, or dynamic WHERE conditions.

However, the concatenation of unsanitized input into SQL code almost always allows for the modification of the intended query.

Consider the following insecure Golang code, which constructs a SQL query from user input:

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql"
)

func vulnerableQuery(userInput string) {
	db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/dbname")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Vulnerable: concatenating user input directly into query
	query := "SELECT * FROM users WHERE username = '" + userInput + "'"
	rows, err := db.Query(query)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {
		// process rows
	}
}

In this example, the user input is directly concatenated into the SQL statement, which could allow an attacker to manipulate the query to access unauthorized data or even perform destructive operations.

Remediation

To prevent SQL Injection vulnerabilities, the recommended approach is to use parameterized queries. These structures inherently separate SQL code from data input, neutralizing the potential for input to alter query logic maliciously.

Furthermore, consider these additional precautions:

  • Implement strong input validation and sanitization to enforce expected data patterns and strip potentially harmful characters.

  • Utilize ORM frameworks that abstract direct SQL query writing, inherently minimizing the risk of injection vulnerabilities.

  • Regularly conduct security testing, including SAST reviews, to detect and address potential vulnerabilities early in the development lifecycle.

  • Educate development teams on secure coding practices to integrate security awareness into the development culture.

The recommended way to prevent SQL Injection in Go is by using prepared statements, which safely handle user input by binding variables to placeholders in the SQL query. This ensures that the input is treated as data rather than executable code.

package main

import (
	"database/sql"
	"fmt"
	"log"

	_ "github.com/go-sql-driver/mysql"
)

func secureQuery(userInput string) {
	db, err := sql.Open("mysql", "username:password@tcp(localhost:3306)/dbname")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Secure: using a prepared statement
	stmt, err := db.Prepare("SELECT * FROM users WHERE username = ?")
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()

	rows, err := stmt.Query(userInput)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()

	for rows.Next() {
		// process rows
	}
}

Configuration

The detector has the following configurable parameters:

  • sources, that indicates the source kinds to check.

  • neutralizations, that indicates the neutralization kinds to check.

Unless you need to change the default behavior, you typically do not need to configure this detector.

References