All skills
asyrafhussin avatar

/laravel-owasp-security

@ac26821

OWASP Top 10 security audit and secure coding guidelines for Laravel + React/Inertia.js applications. Use when auditing for vulnerabilities ("run OWASP audit", "security review", "check my app security") or writing secure Laravel code involving auth, payments, file uploads, or API design. Triggers on security-related tasks, payment handling, authentication, or any request to audit a Laravel codebase.

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/laravel-owasp-security

This session only. Nothing lands on disk.

rulessec-injection-prevention.md

≈1.5k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Prevent SQL Injection and Mass Assignment

Impact: CRITICAL (Prevents database compromise and unauthorized field manipulation)

Why It Matters

  • Risk (SQL Injection): Attackers read, modify, or delete any data in the database, or execute OS commands, by injecting SQL into raw queries
  • Risk (Mass Assignment): Attackers set fields they should not have access to — is_admin, role, user_id — by sending extra POST parameters
  • Impact: Full database exfiltration, privilege escalation, unauthorized data modification
  • OWASP: A03:2021 — Injection

Incorrect — SQL Injection

<?php

// ❌ String concatenation in raw query — SQL injectable
class ClassController extends Controller
{
    public function index(Request $request)
    {
        $sort = $request->input('sort');

        // Attacker sends: sort=name; DROP TABLE users; --
        $classes = DB::select("SELECT * FROM classes ORDER BY {$sort}");
    }
}
<?php

// ❌ User input in whereRaw without binding
$classes = Class::whereRaw("name LIKE '%" . $request->search . "%'")->get();
// Attacker sends: search=' OR '1'='1
<?php

// ❌ orderByRaw with unvalidated user input
$results = Payment::orderByRaw($request->input('column') . ' ' . $request->input('direction'))->get();

Problems:

  • Any user input concatenated into a SQL string is injectable
  • whereRaw, selectRaw, orderByRaw all pass directly to the database engine
  • Attacker can exfiltrate all data, bypass WHERE clauses, or run destructive queries

Correct — SQL Injection Prevention

<?php

declare(strict_types=1);

// ✅ Use parameterized bindings in raw queries
$classes = DB::select('SELECT * FROM classes WHERE status = ?', [$request->status]);

// ✅ Named bindings
$classes = DB::select(
    'SELECT * FROM classes WHERE status = :status AND teacher_id = :teacher',
    ['status' => $request->status, 'teacher' => auth()->id()],
);

// ✅ whereRaw with bindings
$classes = Class::whereRaw('name LIKE ?', ['%' . $request->search . '%'])->get();

// ✅ Whitelist-validate column names before use in orderByRaw
$allowedColumns = ['name', 'created_at', 'price'];
$allowedDirections = ['asc', 'desc'];

$column    = in_array($request->column, $allowedColumns) ? $request->column : 'created_at';
$direction = in_array($request->direction, $allowedDirections) ? $request->direction : 'asc';

$payments = Payment::orderByRaw("{$column} {$direction}")->get();

Incorrect — Mass Assignment

<?php

// ❌ $guarded = [] — all fields are mass assignable, including is_admin, role
class User extends Model
{
    protected $guarded = [];
}

// Attacker sends POST: { "name": "John", "is_admin": true }
User::create($request->all());  // is_admin is now set to true
<?php

// ❌ $request->all() passed directly to create/update
class PostController extends Controller
{
    public function store(Request $request)
    {
        Post::create($request->all());  // Any field can be set by the attacker
    }
}
<?php

// ❌ forceFill with unvalidated user input
$user->forceFill($request->all())->save();  // Bypasses $fillable entirely

Problems:

  • $guarded = [] allows attackers to set any field including is_admin, role, user_id
  • $request->all() passes every submitted parameter directly to the model
  • forceFill with unvalidated data completely bypasses Laravel's mass assignment protection

Correct — Mass Assignment Prevention

<?php

declare(strict_types=1);

// ✅ Define $fillable explicitly — only user-submittable fields
class Post extends Model
{
    protected $fillable = [
        'title',
        'body',
        'category_id',
    ];
    // user_id, published_at, is_featured are NOT in fillable
}
<?php

declare(strict_types=1);

// ✅ Always use $request->validated() — only validated fields pass through
class PostController extends Controller
{
    public function store(StorePostRequest $request): RedirectResponse
    {
        $post = Post::create([
            ...$request->validated(),
            'user_id' => auth()->id(),  // Set sensitive fields explicitly
        ]);

        return redirect()->route('posts.show', $post);
    }
}

class StorePostRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'title'       => ['required', 'string', 'max:255'],
            'body'        => ['required', 'string'],
            'category_id' => ['required', 'integer', 'exists:categories,id'],
            // user_id is NOT here — cannot be submitted by attackers
        ];
    }
}
<?php

declare(strict_types=1);

// ✅ forceFill only with hardcoded fields — never with user input
class NewPasswordController extends Controller
{
    public function store(Request $request): RedirectResponse
    {
        // SAFE — hardcoded field list, not user-controlled
        $user->forceFill([
            'password'       => Hash::make($request->password),
            'remember_token' => Str::random(60),
        ])->save();
    }
}

Recommended Patterns

Pattern Use Case
whereRaw('col = ?', [$value]) Raw SQL with user input
Whitelist array for column names Dynamic ORDER BY / WHERE column
$fillable = ['field1', 'field2'] All models — explicit allowlist
$request->validated() Mass operations in controllers
Set user_id explicitly Ownership fields — never in fillable

Reference: OWASP Laravel Cheat Sheet | Laravel Eloquent Mass Assignment

Source: SKILL.md on GitHub

1 warning16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The analyzed skill is a security auditing and secure coding reference toolkit designed specifically for Laravel 13 + React/Inertia.js environments. It operates using standard pattern matching and reference documentation to provide static code reviews without executing external code or performing suspicious administrative operations. No safety violations or malicious behaviors were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    6/14 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at ac26821. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
Other metadata
metadata
{
  "author": "AsyrafHussin",
  "version": "1.0.3",
  "laravelVersion": "13.x",
  "phpVersion": "8.3+"
}

README badge

README badge for asyrafhussin/agent-skills/laravel-owasp-security