go.aws-lambda.security.tainted-sql-string.tainted-sql-string

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Detected user input used to manually construct a SQL string. This is usually bad practice because manual construction could accidentally result in a SQL injection. An attacker could use a SQL injection to steal or modify contents of the database. Instead, use a parameterized query which is available by default in most database engines. Alternatively, consider using an object-relational mapper (ORM) such as Sequelize which will protect your queries.

Run Locally

Run in CI

Defintion

rules:
  - id: tainted-sql-string
    languages:
      - go
    severity: ERROR
    message: Detected user input used to manually construct a SQL string. This is
      usually bad practice because manual construction could accidentally result
      in a SQL injection. An attacker could use a SQL injection to steal or
      modify contents of the database. Instead, use a parameterized query which
      is available by default in most database engines. Alternatively, consider
      using an object-relational mapper (ORM) such as Sequelize which will
      protect your queries.
    metadata:
      references:
        - https://owasp.org/www-community/attacks/SQL_Injection
      category: security
      owasp:
        - A01:2017 - Injection
        - A03:2021 - Injection
      cwe:
        - "CWE-89: Improper Neutralization of Special Elements used in an SQL
          Command ('SQL Injection')"
      technology:
        - aws-lambda
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: HIGH
      impact: MEDIUM
      confidence: MEDIUM
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - SQL Injection
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern-inside: |
                  func $HANDLER($CTX $CTXTYPE, $EVENT $TYPE, ...) {...}
                  ...
                  lambda.Start($HANDLER, ...)
              - patterns:
                  - pattern-inside: |
                      func $HANDLER($EVENT $TYPE) {...}
                      ...
                      lambda.Start($HANDLER, ...)
                  - pattern-not-inside: |
                      func $HANDLER($EVENT context.Context) {...}
                      ...
                      lambda.Start($HANDLER, ...)
          - focus-metavariable: $EVENT
    pattern-sinks:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern: |
                      "$SQLSTR" + ...
                  - metavariable-regex:
                      metavariable: $SQLSTR
                      regex: (?i)(\s*select|\s*delete|\s*insert|\s*create|\s*update|\s*alter|\s*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-not-inside: |
              log.$PRINT(...)
    pattern-sanitizers:
      - pattern: strconv.Atoi(...)

Examples

tainted-sql-string.go

package main

import (
	"database/sql"
	"encoding/json"
	"log"
	"os"
	"strconv"

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

var (
	db               *sql.DB
	err              error
	connectionString string
	dbUser           string
	dbPass           string
	dataSource       string
)

type Employee struct {
	EmployeeNo int    `json:"emp_no"`
	FirstName  string `json:"first_name"`
	LastName   string `json:"last_name"`
}

func init() {
	connectionString = os.Getenv("CONN")
	dbUser = os.Getenv("DBUSER")
	dbPass = os.Getenv("DBPASS")
	dataSource = dbUser + ":" + dbPass + "@tcp(" + connectionString + ")/employees"
}

func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {

	searchCriteria := request.Body

	db, err = sql.Open("mysql", dataSource)
	if err != nil {
		panic(err.Error())
	}

	defer db.Close()

	// ruleid: tainted-sql-string
	results, err := db.Query("select e.emp_no, e.first_name, e.last_name " +
		"from employees e, departments d, dept_emp de " +
		"where e.last_name LIKE '" + searchCriteria + "%';")

	if err != nil {
		log.Fatal(err)
	}
	defer results.Close()
	// ruleid: tainted-sql-string
	_, err = db.Exec(` 
  DELETE FROM table WHERE Id = ` + request.Get("Id"))
	// ruleid: tainted-sql-string
	_, err = db.Exec("DELETE FROM table WHERE Id = " + request.Get("Id"))

	// ok: tainted-sql-string
	log.Printf("DELETE FROM table WHERE Id = " + request.Get("Id"))
	// ok: tainted-sql-string
	_, err = db.Exec(` FAKE
  DELETE FROM table WHERE Id = ` + request.Get("Id"))

	idhtml := request.Get("Id")
	id, _ := strconv.Atoi(idhtml)

	// ok: tainted-sql-string
	_, err = db.Exec("DELETE FROM table WHERE Id = " + id)

	// ok: tainted-sql-string
	results2, err2 := db.Query("select * from foobar")

	employees := make([]Employee, 0)

	for results.Next() {
		var e Employee

		err := results.Scan(&e.EmployeeNo, &e.FirstName, &e.LastName)
		if err != nil {
			log.Fatal(err)
		}
		employees = append(employees, e)
	}

	data, _ := json.Marshal(employees)

	return events.APIGatewayProxyResponse{
		StatusCode:      200,
		Body:            string(data),
		IsBase64Encoded: false,
	}, nil
}

func main() {
	lambda.Start(handler)
}