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);