SQL injection

SQL injection

Description

SQL injection occurs when user input is concatenated or formatted directly into an executed SQL statement. For example, building "SELECT ... WHERE id = " + userInput lets input such as 1 OR 1=1 or 1; DROP TABLE users-- change query structure. Authentication bypass and data disclosure, modification or deletion depend on database permissions, the driver and its settings. Support for executing multiple statements also depends on the driver and configuration.

Potential impact

  • Attackers may retrieve other users' personal data, account information or internal settings.
  • Available database permissions may allow data modification, deletion or dropping tables and schemas.
  • Injected conditions such as OR 1=1 may bypass authentication or authorization checks.
  • Expensive queries or destructive operations may disrupt the database and service.

Remediation

  • Use driver-supported placeholders such as ? or $1 in db.Query, db.QueryRow and db.Exec, and supply values as separate arguments.
  • Use value-binding APIs in ORMs and query builders too; never concatenate user input into raw SQL.
  • Validate numeric IDs with functions such as strconv.Atoi and handle conversion failures.
  • Select SQL elements that cannot be bound, such as column names or sort directions, only from a code-defined allow-list.
  • Do not give the database account unnecessary permissions such as DROP or ALTER.
  • Keep detailed database errors in protected internal logs and return generic errors to users.

Examples

These handler excerpts compare SQL parameter passing. They assume a driver that supports ?; use the placeholder syntax required by the actual driver. Authentication, authorization and response output handling are separate requirements.

Before

go
package main

import (
    "database/sql"
    "net/http"
)

// Before: a handler that concatenates input into SQL
func unsafeUserDetailHandler(db *sql.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // Read id directly as a query-parameter string
        userID := r.URL.Query().Get("id")

        // Unsafe: build SQL by concatenating user input
        // Example: /user?id=1 OR 1=1 --
        query := "SELECT name, email FROM users WHERE id = " + userID

        row := db.QueryRow(query)
        var name, email string
        if err := row.Scan(&name, &email); err != nil {
            http.Error(w, "error", http.StatusInternalServerError)
            return
        }

        w.Write([]byte(name + " / " + email))
    }
}

After

go
package main

import (
    "database/sql"
    "net/http"
    "strconv"
)

// After: a handler using a parameterized statement
func safeUserDetailHandler(db *sql.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        userIDStr := r.URL.Query().Get("id")

        // 1. Validate the type: reject non-numeric input
        userID, err := strconv.Atoi(userIDStr)
        if err != nil {
            http.Error(w, "invalid id", http.StatusBadRequest)
            return
        }

        // 2. Use a parameterized statement and placeholder
        //   - Bind values to placeholders such as `?` or `$1`
        //     so their string contents cannot change the query structure
        const query = "SELECT name, email FROM users WHERE id = ?"

        row := db.QueryRow(query, userID)
        var name, email string
        if err := row.Scan(&name, &email); err != nil {
            http.Error(w, "not found", http.StatusNotFound)
            return
        }

        w.Write([]byte(name + " / " + email))
    }
}

Explanation:

  • Before: The id input is concatenated into SQL and can alter its condition. OR 1=1 may select more rows, but this QueryRow call reads only the first and discards the rest. Multiple-statement attacks depend on driver settings and database permissions.
  • After: strconv.Atoi checks the numeric type, and userID is passed separately to fixed SQL so it is treated as data. Production error handling should distinguish sql.ErrNoRows from a database failure.

References