go.lang.security.injection.tainted-sql-string.tainted-sql-string

profile photo of semgrepsemgrep
Author
unknown
Download Count*

User data flows into this manually-constructed SQL string. User data can be safely inserted into SQL strings using prepared statements or an object-relational mapper (ORM). Manually-constructed SQL strings is a possible indicator of SQL injection, which could let an attacker steal or manipulate data from the database. Instead, use prepared statements (db.Query("SELECT * FROM t WHERE id = ?", id)) or a safe library.

Run Locally

Run in CI

Defintion

rules:
  - id: tainted-sql-string
    languages:
      - go
    message: User data flows into this manually-constructed SQL string. User data
      can be safely inserted into SQL strings using prepared statements or an
      object-relational mapper (ORM). Manually-constructed SQL strings is a
      possible indicator of SQL injection, which could let an attacker steal or
      manipulate data from the database. Instead, use prepared statements
      (`db.Query("SELECT * FROM t WHERE id = ?", id)`) or a safe library.
    options:
      interfile: true
    metadata:
      cwe:
        - "CWE-89: Improper Neutralization of Special Elements used in an SQL
          Command ('SQL Injection')"
      owasp:
        - A01:2017 - Injection
        - A03:2021 - Injection
      references:
        - https://golang.org/doc/database/sql-injection
        - https://www.stackhawk.com/blog/golang-sql-injection-guide-examples-and-prevention/
      category: security
      technology:
        - go
      confidence: HIGH
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: HIGH
      impact: MEDIUM
      interfile: true
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - SQL Injection
    mode: taint
    severity: ERROR
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern: |
                  ($REQUEST : *http.Request).$ANYTHING
              - pattern: |
                  ($REQUEST : http.Request).$ANYTHING
          - metavariable-regex:
              metavariable: $ANYTHING
              regex: ^(BasicAuth|Body|Cookie|Cookies|Form|FormValue|GetBody|Host|MultipartReader|ParseForm|ParseMultipartForm|PostForm|PostFormValue|Referer|RequestURI|Trailer|TransferEncoding|UserAgent|URL)$
    pattern-sinks:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-either:
                      - pattern: |
                          "$SQLSTR" + ...
                      - patterns:
                          - pattern-inside: |
                              $VAR = "$SQLSTR";
                              ...
                          - pattern: $VAR += ...
                      - patterns:
                          - pattern-inside: |
                              var $SB strings.Builder
                              ...
                          - pattern-inside: |
                              $SB.WriteString("$SQLSTR")
                              ...
                              $SB.String(...)
                          - pattern: |
                              $SB.WriteString(...)
                  - metavariable-regex:
                      metavariable: $SQLSTR
                      regex: (?i)(select|delete|insert|create|update|alter|drop).*
              - patterns:
                  - pattern-either:
                      - pattern: fmt.Fprintf($F, "$SQLSTR", ...)
                      - pattern: fmt.Sprintf("$SQLSTR", ...)
                      - pattern: fmt.Printf("$SQLSTR", ...)
                  - metavariable-regex:
                      metavariable: $SQLSTR
                      regex: \s*(?i)(select|delete|insert|create|update|alter|drop)\b.*%(v|s|q).*
    pattern-sanitizers:
      - pattern-either:
          - pattern: strconv.Atoi(...)
          - pattern: |
              ($X: bool)

Examples

tainted-sql-string.go

package main

import (
    "crypto/tls"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "net/url"
    "database/sql"
)

func DeleteHandler(db *sql.DB) func(w http.ResponseWriter, req *http.Request) {
    return func(w http.ResponseWriter, req *http.Request) {
        del := req.URL.Query().Get("del")
        id := req.URL.Query().Get("Id")
        if del == "del" {
            // ruleid: tainted-sql-string
            _, err = db.Exec("DELETE FROM table WHERE Id = " + id)
            if err != nil {
                panic(err)
            }
        }
    }
}

func DeleteHandlerOk(db *sql.DB) func(w http.ResponseWriter, req *http.Request) {
    return func(w http.ResponseWriter, req *http.Request) {
        del := req.URL.Query().Get("del")
        idhtml := req.URL.Query().Get("Id")

        id, _ := strconv.Atoi(idhtml)

        if del == "del" {
            // ok: tainted-sql-string
            _, err = db.Exec("DELETE FROM table WHERE Id = " + id)
            if err != nil {
                panic(err)
            }
        }
    }
}

func SelectHandler(db *sql.DB) func(w http.ResponseWriter, req *http.Request) {
    return func(w http.ResponseWriter, req *http.Request) {
        del := req.URL.Query().Get("del")
        id := req.URL.Query().Get("Id")
        if del == "del" {
            // ruleid: tainted-sql-string
            sql := fmt.Sprintf("SELECT * FROM table WHERE Id = %v", id)
            _, err = db.Exec(sql)
            if err != nil {
                panic(err)
            }
        }
    }
}

func SelectHandler2(db *sql.DB) func(w http.ResponseWriter, req *http.Request) {
    return func(w http.ResponseWriter, req *http.Request) {
        var sb strings.Builder
        del := req.URL.Query().Get("del")
        id := req.URL.Query().Get("Id")
        if del == "del" {
            sb.WriteString("SELECT * FROM table WHERE Id = ")
            // ruleid: tainted-sql-string
            sb.WriteString(id)

            sql := sb.String()
            _, err = db.Exec(sql)
            if err != nil {
                panic(err)
            }
        }
    }
}

func SelectHandler2ok(db *sql.DB) func(w http.ResponseWriter, req *http.Request) {
    return func(w http.ResponseWriter, req *http.Request) {
        var sb strings.Builder
        del := req.URL.Query().Get("del")
        id := req.URL.Query().Get("Id")
        if del == "del" {
            sb.WriteString("SELECT * FROM table WHERE Id = ")
            // ok: tainted-sql-string
            sb.WriteString(id)

            sql := "select hello"
            _, err = db.Exec(sql)
            if err != nil {
                panic(err)
            }
        }
    }
}

func SelectHandler3(db *sql.DB) func(w http.ResponseWriter, req *http.Request) {
    return func(w http.ResponseWriter, req *http.Request) {
        del := req.URL.Query().Get("del")
        id := req.URL.Query().Get("Id")
        if del == "del" {
            sql := "SELECT * FROM table WHERE Id = "
            // ruleid: tainted-sql-string
            sql += id
            _, err = db.Exec(sql)
            if err != nil {
                panic(err)
            }
        }
    }
}

func SelectHandler3(db *sql.DB) func(w http.ResponseWriter, req *http.Request) {
    return func(w http.ResponseWriter, req *http.Request) {
        del := req.URL.Query().Get("del")
        id := req.URL.Query().Get("Id")
        if del == "del" {
            sql := "SELECT * FROM table WHERE Id = "
            // ok: tainted-sql-string
            sql += (id != 3)
            _, err = db.Exec(sql)
            if err != nil {
                panic(err)
            }
        }
    }
}

func SelectHandlerOk(db *sql.DB) func(w http.ResponseWriter, req *http.Request) {
    return func(w http.ResponseWriter, req *http.Request) {
        del := req.URL.Query().Get("del")
        id := req.URL.Query().Get("Id")

        if del == "del" {
            // ok: tainted-sql-string
            _, err = db.QueryRow("SELECT * FROM table WHERE Id = $1", id)

            // ok: tainted-sql-string
            fmt.Fprintf(w, "Deleted %s", id)
            if err != nil {
                panic(err)
            }
        }
    }
}