# How to Prevent SQL Injection in Laravel

> SQL injection vulnerabilities in raw queries and improper Eloquent usage can expose your database. Learn how to write secure queries.

**Severity:** critical | **Category:** Application Security

---

## The Issue

SQL injection allows attackers to execute arbitrary SQL commands on your database by injecting malicious input through your application. While Laravel's Eloquent ORM uses parameterized queries by default, SQL injection vulnerabilities are introduced when developers use raw queries, DB::raw(), whereRaw(), or string concatenation with user input. A successful SQL injection can read, modify, or delete all data in your database.

## Steps to Fix

### 1. Use parameterized queries instead of string concatenation

Never concatenate user input into SQL queries. Replace:

// VULNERABLE - never do this
$users = DB::select("SELECT * FROM users WHERE email = '" . $request->email . "'");

With parameterized binding:

// SAFE - use parameter binding
$users = DB::select('SELECT * FROM users WHERE email = ?', [$request->email]);

// SAFE - use named bindings
$users = DB::select('SELECT * FROM users WHERE email = :email', ['email' => $request->email]);

### 2. Secure raw query methods

When using whereRaw(), orderByRaw(), or DB::raw(), always use bindings:

// VULNERABLE
User::whereRaw("email = '" . $request->email . "'")->first();

// SAFE
User::whereRaw('email = ?', [$request->email])->first();

// VULNERABLE - column names cannot be parameterized
User::orderByRaw($request->sort_column . ' ' . $request->sort_direction)->get();

// SAFE - whitelist allowed values
$column = in_array($request->sort_column, ['name', 'email', 'created_at']) ? $request->sort_column : 'created_at';
$direction = $request->sort_direction === 'desc' ? 'desc' : 'asc';
User::orderByRaw("$column $direction")->get();

### 3. Validate and sanitize all input

Use Laravel validation to restrict input before it reaches queries:

$validated = $request->validate([
    'email' => 'required|email|max:255',
    'id' => 'required|integer',
    'status' => 'required|in:active,inactive,pending',
    'search' => 'nullable|string|max:100',
]);

// Use validated data in queries
$user = User::where('email', $validated['email'])->first();

Validation prevents unexpected input types and lengths from reaching your database layer.

### 4. Use Eloquent ORM whenever possible

Eloquent automatically parameterizes all queries:

// All of these are safe from SQL injection
User::find($id);
User::where('email', $email)->first();
User::where('status', $status)->where('role', $role)->get();
User::whereBetween('created_at', [$start, $end])->get();

Avoid raw queries unless Eloquent genuinely cannot express what you need. For complex queries, use the query builder with bindings rather than raw SQL strings.

## Verification

Test your forms by submitting SQL injection payloads in input fields:

' OR '1'='1
'; DROP TABLE users; --
1 UNION SELECT * FROM users

Your application should handle these as normal (invalid) input without errors or unexpected behavior. If you see database errors or unexpected data returned, you have a SQL injection vulnerability.

## Prevention

Establish a coding standard that prohibits string concatenation in queries. Use static analysis tools like PHPStan or Psalm to detect unsafe query patterns. Code review all uses of DB::raw(), whereRaw(), selectRaw(), and havingRaw(). Run regular security audits of your codebase.

---

## Frequently Asked Questions

### Is Eloquent completely safe from SQL injection?

Standard Eloquent methods (where, find, create, update) are safe because they use parameterized queries. However, methods that accept raw SQL (whereRaw, orderByRaw, DB::raw, selectRaw) can be vulnerable if you pass unsanitized user input. Column names and table names cannot be parameterized and must be whitelisted.

### Can SQL injection happen through URL parameters?

Yes. Any user-controlled input that reaches a database query is a potential injection point, including URL parameters, query strings, form fields, headers, cookies, and even uploaded file names. Always validate and parameterize regardless of the input source.

### How do I audit my existing code for SQL injection?

Search your codebase for DB::raw, whereRaw, selectRaw, orderByRaw, havingRaw, DB::select, DB::statement, and DB::unprepared. Check each usage for user input being concatenated rather than bound. Tools like PHPStan with the Larastan extension can help identify some patterns automatically.

