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

profile photo of returntocorpreturntocorp
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.
    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
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
    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: |
                      "$SQLSTR" + ...
                  - 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: strconv.Atoi(...)

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 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)
            }
        }
    }
}