Skip Content

A05: Injection (Blog)

What is injection?

Injection happens when untrusted input is interpreted as code, a query fragment, or a command. In Laravel it most commonly shows up as SQL injection, command injection, and XSS introduced by bypassing Blade escaping.

Laravel explicitly warns that you must never pass user-controlled input into Rule::unique()->ignore(...), because it can create SQL injection risk.

Typical Laravel failure (blog)

  • Raw SQL with interpolated input.
  • Unsafe ignore() usage in validation.
  • Unsafe shell execution.
  • Unsafe dynamic column/order inputs in "list posts" endpoints.
  • Stored XSS from rendering raw user comments with {!! !!}.

Impact

  • Data theft or data corruption.
  • Account takeover via injected scripts (XSS).
  • Remote code execution via command injection.
  • Compliance and incident-response fallout.

Laravel 12 remediation

  • Prefer Eloquent / Query Builder bindings.
  • Avoid DB::raw() unless unavoidable.
  • Never pass user input to column names, sort clauses, raw expressions, or shell commands without strict allowlists.
  • Use Form Requests and validate before query construction.
  • Keep Blade escaped by default; use {!! !!} only for sanitized trusted HTML.

Concrete fix

Safe bindings (no string interpolation):

$query->where('email', $request->string('email'));

Safe sorting for post indexes with an allowlist:

$allowedSorts = ['title', 'created_at'];
 
$sort = in_array($request->sort, $allowedSorts, true)
? $request->sort
: 'created_at';
 
$query->orderBy($sort);

Avoid stored XSS in comments: keep Blade escaped by default and store plain text.

{{-- Good: escaped output (default) --}}
<p>{{ $comment->body }}</p>
 
{{-- Bad: raw HTML output from untrusted users --}}
{{-- <p>{!! $comment->body !!}</p> --}}

Design pattern angle

Use Strategy for safe filtering/sorting rules instead of concatenating query fragments in controllers. Keep the allowlist and mapping logic centralized, explicit, and testable.

use Illuminate\Database\Eloquent\Builder;
 
interface SortStrategy
{
public function apply(Builder $query, string $direction = 'desc'): Builder;
}
 
final class SortByCreatedAt implements SortStrategy
{
public function apply(Builder $query, string $direction = 'desc'): Builder
{
return $query->orderBy('created_at', $direction);
}
}
 
final class SortByName implements SortStrategy
{
public function apply(Builder $query, string $direction = 'asc'): Builder
{
return $query->orderBy('name', $direction);
}
}
 
// Usage: map user input to known strategies
$sorts = [
'created_at' => new SortByCreatedAt(),
'name' => new SortByName(),
];
 
$sortKey = $request->string('sort')->toString();
$sort = $sorts[$sortKey] ?? $sorts['created_at'];
 
$query = $sort->apply($query);

References