php.laravel.security.laravel-sql-injection.laravel-sql-injection

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Detected a SQL query based on user input. This could lead to SQL injection, which could potentially result in sensitive data being exfiltrated by attackers. Instead, use parameterized queries and prepared statements.

Run Locally

Run in CI

Defintion

rules:
  - id: laravel-sql-injection
    metadata:
      owasp:
        - A01:2017 - Injection
        - A03:2021 - Injection
      cwe:
        - "CWE-89: Improper Neutralization of Special Elements used in an SQL
          Command ('SQL Injection')"
      category: security
      technology:
        - laravel
      references:
        - https://laravel.com/docs/8.x/queries
      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
    severity: WARNING
    message: Detected a SQL query based on user input. This could lead to SQL
      injection, which could potentially result in sensitive data being
      exfiltrated by attackers. Instead, use parameterized queries and prepared
      statements.
    languages:
      - php
    mode: taint
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern: $_GET
              - pattern: $_POST
              - pattern: $_COOKIE
              - pattern: $_REQUEST
              - pattern: $_SERVER
    pattern-sinks:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern: $SQL
                  - pattern-either:
                      - pattern-inside: DB::table(...)->whereRaw($SQL, ...)
                      - pattern-inside: DB::table(...)->orWhereRaw($SQL, ...)
                      - pattern-inside: DB::table(...)->groupByRaw($SQL, ...)
                      - pattern-inside: DB::table(...)->havingRaw($SQL, ...)
                      - pattern-inside: DB::table(...)->orHavingRaw($SQL, ...)
                      - pattern-inside: DB::table(...)->orderByRaw($SQL, ...)
              - patterns:
                  - pattern: $EXPRESSION
                  - pattern-either:
                      - pattern-inside: DB::table(...)->selectRaw($EXPRESSION, ...)
                      - pattern-inside: DB::table(...)->fromRaw($EXPRESSION, ...)
              - patterns:
                  - pattern: $COLUMNS
                  - pattern-either:
                      - pattern-inside: DB::table(...)->whereNull($COLUMNS, ...)
                      - pattern-inside: DB::table(...)->orWhereNull($COLUMN)
                      - pattern-inside: DB::table(...)->whereNotNull($COLUMNS, ...)
                      - pattern-inside: DB::table(...)->whereRowValues($COLUMNS, ...)
                      - pattern-inside: DB::table(...)->orWhereRowValues($COLUMNS, ...)
                      - pattern-inside: DB::table(...)->find($ID, $COLUMNS)
                      - pattern-inside: DB::table(...)->paginate($PERPAGE, $COLUMNS, ...)
                      - pattern-inside: DB::table(...)->simplePaginate($PERPAGE, $COLUMNS, ...)
                      - pattern-inside: DB::table(...)->cursorPaginate($PERPAGE, $COLUMNS, ...)
                      - pattern-inside: DB::table(...)->getCountForPagination($COLUMNS)
                      - pattern-inside: DB::table(...)->aggregate($FUNCTION, $COLUMNS)
                      - pattern-inside: DB::table(...)->numericAggregate($FUNCTION, $COLUMNS)
                      - pattern-inside: DB::table(...)->insertUsing($COLUMNS, ...)
                      - pattern-inside: DB::table(...)->select($COLUMNS)
                      - pattern-inside: DB::table(...)->get($COLUMNS)
                      - pattern-inside: DB::table(...)->count($COLUMNS)
              - patterns:
                  - pattern: $COLUMN
                  - pattern-either:
                      - pattern-inside: DB::table(...)->whereIn($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereIn($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereNotIn($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereNotIn($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereIntegerInRaw($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereIntegerInRaw($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereIntegerNotInRaw($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereIntegerNotInRaw($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereBetweenColumns($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereBetween($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereBetweenColumns($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereNotBetween($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereNotBetweenColumns($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereNotBetween($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereNotBetweenColumns($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereNotNull($COLUMN)
                      - pattern-inside: DB::table(...)->whereDate($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereDate($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereTime($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereTime($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereDay($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereDay($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereMonth($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereMonth($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereYear($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereYear($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereJsonContains($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereJsonContains($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereJsonDoesntContain($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereJsonDoesntContain($COLUMN, ...)
                      - pattern-inside: DB::table(...)->whereJsonLength($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhereJsonLength($COLUMN, ...)
                      - pattern-inside: DB::table(...)->having($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orHaving($COLUMN, ...)
                      - pattern-inside: DB::table(...)->havingBetween($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orderBy($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orderByDesc($COLUMN)
                      - pattern-inside: DB::table(...)->latest($COLUMN)
                      - pattern-inside: DB::table(...)->oldest($COLUMN)
                      - pattern-inside: DB::table(...)->forPageBeforeId($PERPAGE, $LASTID, $COLUMN)
                      - pattern-inside: DB::table(...)->forPageAfterId($PERPAGE, $LASTID, $COLUMN)
                      - pattern-inside: DB::table(...)->value($COLUMN)
                      - pattern-inside: DB::table(...)->pluck($COLUMN, ...)
                      - pattern-inside: DB::table(...)->implode($COLUMN, ...)
                      - pattern-inside: DB::table(...)->min($COLUMN)
                      - pattern-inside: DB::table(...)->max($COLUMN)
                      - pattern-inside: DB::table(...)->sum($COLUMN)
                      - pattern-inside: DB::table(...)->avg($COLUMN)
                      - pattern-inside: DB::table(...)->average($COLUMN)
                      - pattern-inside: DB::table(...)->increment($COLUMN, ...)
                      - pattern-inside: DB::table(...)->decrement($COLUMN, ...)
                      - pattern-inside: DB::table(...)->where($COLUMN, ...)
                      - pattern-inside: DB::table(...)->orWhere($COLUMN, ...)
                      - pattern-inside: DB::table(...)->addSelect($COLUMN)
              - patterns:
                  - pattern: $QUERY
                  - pattern-inside: DB::unprepared($QUERY)

Examples

laravel-sql-injection.php

<?php

$tainted = $_GET['userinput'];

// https://laravel.com/docs/8.x/database
// Since unprepared statements do not bind parameters, they may be vulnerable to SQL injection. You should never allow user controlled values within an unprepared statement.
// ruleid: laravel-sql-injection
DB::unprepared("update users set votes = 100 where name = '$tainted'");

// https://laravel.com/docs/8.x/queries
// PDO does not support binding column names. Therefore, you should never allow user input to dictate the column names referenced by your queries, including "order by" columns.
// ruleid: laravel-sql-injection
$user = DB::table('users')->where($tainted, 'John')->first();
// ruleid: laravel-sql-injection
$titles = DB::table('users')->pluck($tainted);
// ruleid: laravel-sql-injection
DB::table('users')->orderBy($tainted);
// ruleid: laravel-sql-injection
$price = DB::table('orders')->max($tainted);
// ruleid: laravel-sql-injection
$query = DB::table('users')->select($tainted);

// ok: laravel-sql-injection
$user = DB::table('users')->where('name', $tainted)->first();

// https://laravel.com/docs/8.x/queries
// Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities.
// ruleid: laravel-sql-injection
$users = DB::table('users')->select(DB::raw($tainted));
// ruleid: laravel-sql-injection
$orders = DB::table('orders')->selectRaw($tainted);
// ruleid: laravel-sql-injection
$orders = DB::table('orders')->whereRaw($tainted);

// ok: laravel-sql-injection
$orders = DB::table('orders')->selectRaw('price * ? as price_with_tax', [$tainted]);