Skip to content
Pro Analyzer — Available with ShieldCI Pro

Column Name SQL Injection Analyzer

Analyzer IDCategorySeverityTime To Fix
column-name-sql-injection🛡️ SecurityCritical10 minutes

What This Checks

Detects SQL injection vulnerabilities where user input controls column names in Laravel database queries. PDO does not support binding column names, only values, making column name injection a unique and dangerous vulnerability.

Covered methods (14 + joins):

Column name methods (first argument checked):

  • orderBy, orderByDesc - column name in first argument
  • select, addSelect - any request usage flagged
  • groupBy - any request usage flagged
  • pluck - first argument and second argument ($key column) checked
  • where, whereColumn - first argument (column name) checked
  • latest, oldest, reorder - column name argument checked
  • value - column name argument checked
  • having, orHaving - first argument (column name) checked

Join methods (second and fourth arguments checked):

  • join, leftJoin, rightJoin - $first and $second column args in ON clause

Additional detection:

  • when() closures - detects user input inside closures including concatenated input ($prefix . $request->sort)

Excluded from detection:

Allowlist suppression:

  • A file containing in_array($var, $allowed, true) (strict comparison) is treated as allowlist-validated and skipped. For per-line suppression, add // @shieldci-ignore on the affected line.

Why It Matters

  • SQL Injection: Column names cannot be safely bound by PDO, creating a direct SQL injection vulnerability
  • Data Exfiltration: Attackers can manipulate queries to extract sensitive data from unexpected columns
  • Database Structure Discovery: Malicious users can probe your database schema through error messages
  • Authorization Bypass: Column manipulation can bypass access controls and retrieve unauthorized data

Unlike parameter SQL injection (which PDO protects against), column name injection occurs when user input dictates which columns are used in SQL queries. Since PDO can only bind values, not column or table names, this creates a vulnerability that Laravel's query builder cannot automatically protect against.

How to Fix

Quick Fix (2 minutes)

Use a whitelist of allowed columns instead of passing user input directly:

php
// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index(Request $request)
    {
        // ❌ VULNERABLE: Direct user input
        // $users = User::orderBy($request->input('sort'))->get();

        // ✅ SAFE: Whitelist allowed columns
        $allowedColumns = ['name', 'email', 'created_at'];
        $sortColumn = $request->input('sort', 'created_at');

        if (!in_array($sortColumn, $allowedColumns, true)) {
            $sortColumn = 'created_at';
        }

        $users = User::orderBy($sortColumn)->get();

        return response()->json($users);
    }
}

Proper Fix (10 minutes)

Option 1: Whitelist with Validation

Use Laravel's validation to ensure only allowed column names are accepted:

php
// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index(Request $request)
    {
        $validated = $request->validate([
            'sort' => 'sometimes|in:name,email,created_at',
            'direction' => 'sometimes|in:asc,desc',
        ]);

        $sortColumn = $validated['sort'] ?? 'created_at';
        $direction = $validated['direction'] ?? 'asc';

        $users = User::orderBy($sortColumn, $direction)->get();

        return response()->json($users);
    }
}

Option 2: Switch Statement Mapping

Map user input to safe column names using a switch or match statement:

php
// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index(Request $request)
    {
        $sortInput = $request->input('sort', 'date');

        // Map user-friendly names to actual column names
        $sortColumn = match ($sortInput) {
            'date' => 'created_at',
            'name' => 'name',
            'email' => 'email',
            default => 'created_at',
        };

        $users = User::orderBy($sortColumn)->get();

        return response()->json($users);
    }
}

Option 3: Schema Validation

Validate against actual table columns using Laravel's Schema facade:

php
// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;

class UserController extends Controller
{
    public function index(Request $request)
    {
        $sortColumn = $request->input('sort', 'created_at');

        // Validate column exists in users table
        if (!Schema::hasColumn('users', $sortColumn)) {
            abort(400, 'Invalid sort column');
        }

        // Optional: Whitelist allowed columns even after schema check
        $allowedColumns = ['name', 'email', 'created_at'];
        if (!in_array($sortColumn, $allowedColumns, true)) {
            abort(400, 'Column not allowed for sorting');
        }

        $users = User::orderBy($sortColumn)->get();

        return response()->json($users);
    }
}

Option 4: Request Class Validation

Create a dedicated Form Request for complex validation logic:

php
// app/Http/Requests/UserIndexRequest.php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class UserIndexRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'sort' => ['sometimes', Rule::in(['name', 'email', 'created_at'])],
            'direction' => ['sometimes', Rule::in(['asc', 'desc'])],
        ];
    }

    public function getSortColumn(): string
    {
        return $this->validated('sort', 'created_at');
    }

    public function getSortDirection(): string
    {
        return $this->validated('direction', 'asc');
    }
}

// app/Http/Controllers/UserController.php
namespace App\Http\Controllers;

use App\Http\Requests\UserIndexRequest;
use App\Models\User;

class UserController extends Controller
{
    public function index(UserIndexRequest $request)
    {
        $users = User::orderBy(
            $request->getSortColumn(),
            $request->getSortDirection()
        )->get();

        return response()->json($users);
    }
}

References