Skip to content

🔒 Security Best Practices

Comprehensive security guidelines for Laravel applications. This section covers authentication, authorization, data protection, and vulnerability prevention.

🛡️ Input Validation

Always validate and sanitize user input to prevent security vulnerabilities.

✅ Good Example

php
<?php

declare(strict_types=1);

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateUserRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()->can('create', User::class);
    }

    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255', 'regex:/^[a-zA-Z\s]+$/'],
            'email' => ['required', 'email', 'max:255', 'unique:users,email'],
            'password' => [
                'required',
                'string',
                'min:12',
                'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/',
            ],
            'role' => ['required', 'string', 'in:user,moderator,admin'],
        ];
    }

    public function messages(): array
    {
        return [
            'password.regex' => 'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.',
            'name.regex' => 'Name can only contain letters and spaces.',
        ];
    }

    protected function prepareForValidation(): void
    {
        $this->merge([
            'email' => strtolower(trim($this->email)),
            'name' => trim($this->name),
        ]);
    }
}

❌ Bad Example

php
<?php

// No validation - security risk
class UserController extends Controller
{
    public function store(Request $request)
    {
        // Direct creation without validation
        $user = User::create([
            'name' => $request->name, // No validation
            'email' => $request->email, // No validation
            'password' => $request->password, // No validation
            'role' => $request->role, // No validation
        ]);
        
        return response()->json($user);
    }
}

🔐 Authentication & Authorization

Password Security

✅ Good Example

php
<?php

namespace App\Services;

use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class PasswordService
{
    public function validatePassword(string $password): array
    {
        $validator = Validator::make(['password' => $password], [
            'password' => [
                'required',
                'string',
                'min:12',
                'max:128',
                'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/',
            ],
        ]);

        return [
            'valid' => !$validator->fails(),
            'errors' => $validator->errors()->get('password', []),
        ];
    }

    public function hashPassword(string $password): string
    {
        return Hash::make($password);
    }

    public function verifyPassword(string $password, string $hash): bool
    {
        return Hash::check($password, $hash);
    }
}

❌ Bad Example

php
<?php

// Weak password handling
class UserController extends Controller
{
    public function store(Request $request)
    {
        // Weak password requirements
        $request->validate([
            'password' => 'required|min:6', // Too weak
        ]);
        
        // Direct password storage without proper hashing
        $user = User::create([
            'password' => md5($request->password), // MD5 is not secure
        ]);
    }
}

Authorization Policies

✅ Good Example

php
<?php

namespace App\Policies;

use App\Models\User;
use App\Models\Post;

class PostPolicy
{
    public function viewAny(User $user): bool
    {
        return $user->hasPermission('posts.view');
    }

    public function view(User $user, Post $post): bool
    {
        return $user->hasPermission('posts.view') || 
               $post->user_id === $user->id;
    }

    public function create(User $user): bool
    {
        return $user->hasPermission('posts.create');
    }

    public function update(User $user, Post $post): bool
    {
        return $user->hasPermission('posts.update') || 
               ($post->user_id === $user->id && $user->hasPermission('posts.update.own'));
    }

    public function delete(User $user, Post $post): bool
    {
        return $user->hasPermission('posts.delete') || 
               ($post->user_id === $user->id && $user->hasPermission('posts.delete.own'));
    }
}

❌ Bad Example

php
<?php

// No authorization checks - security risk
class PostController extends Controller
{
    public function update(Request $request, Post $post)
    {
        // No authorization check
        $post->update($request->all());
        
        return response()->json($post);
    }
    
    public function delete(Post $post)
    {
        // No authorization check
        $post->delete();
        
        return response()->json(['message' => 'Post deleted']);
    }
}

🛡️ Data Protection

Sensitive Data Handling

✅ Good Example

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;

class User extends Model
{
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
        'api_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
        'password' => 'hashed',
        'is_active' => 'boolean',
    ];

    protected function password(): Attribute
    {
        return Attribute::make(
            set: fn (string $value) => Hash::make($value),
        );
    }

    protected function email(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => strtolower($value),
            set: fn (string $value) => strtolower(trim($value)),
        );
    }
}

❌ Bad Example

php
<?php

// Exposing sensitive data
class User extends Model
{
    // No fillable array - mass assignment vulnerability
    // No hidden fields - sensitive data exposed
    // No casts - data not properly handled
    
    public function toArray()
    {
        return $this->attributes; // Exposes all data including passwords
    }
}

SQL Injection Prevention

✅ Good Example

php
<?php

namespace App\Repositories;

use App\Models\User;
use Illuminate\Database\Eloquent\Builder;

class UserRepository
{
    public function searchUsers(string $query): Collection
    {
        // Use Eloquent ORM - automatically prevents SQL injection
        return User::where('name', 'LIKE', "%{$query}%")
            ->orWhere('email', 'LIKE', "%{$query}%")
            ->get();
    }

    public function getUsersByRole(string $role): Collection
    {
        // Use parameterized queries
        return User::where('role', $role)
            ->where('is_active', true)
            ->get();
    }
}

❌ Bad Example

php
<?php

// SQL injection vulnerability
class UserController extends Controller
{
    public function search(Request $request)
    {
        $query = $request->get('q');
        
        // Direct SQL query - vulnerable to SQL injection
        $users = DB::select("SELECT * FROM users WHERE name LIKE '%{$query}%'");
        
        return response()->json($users);
    }
}

🔒 CSRF Protection

✅ Good Example

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class VerifyCsrfToken extends Middleware
{
    protected $except = [
        'api/webhooks/*', // Only exclude necessary endpoints
    ];

    public function handle(Request $request, Closure $next)
    {
        if ($this->shouldPassThrough($request)) {
            return $next($request);
        }

        if ($this->isReading($request) || 
            $this->runningUnitTests() || 
            $this->inExceptArray($request) ||
            $this->tokensMatch($request)) {
            return $next($request);
        }

        throw new TokenMismatchException('CSRF token mismatch.');
    }
}

❌ Bad Example

php
<?php

// Disabling CSRF protection - security risk
class VerifyCsrfToken extends Middleware
{
    protected $except = [
        '*', // Disables CSRF for all routes
    ];
}

🛡️ Rate Limiting

✅ Good Example

php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;

class AuthController extends Controller
{
    public function login(Request $request)
    {
        $key = 'login.' . $request->ip();
        
        if (RateLimiter::tooManyAttempts($key, 5)) {
            $seconds = RateLimiter::availableIn($key);
            
            return response()->json([
                'message' => 'Too many login attempts. Please try again in ' . $seconds . ' seconds.',
            ], 429);
        }

        RateLimiter::hit($key, 300); // 5 minutes

        // Login logic here
    }
}

❌ Bad Example

php
<?php

// No rate limiting - vulnerable to brute force attacks
class AuthController extends Controller
{
    public function login(Request $request)
    {
        // No rate limiting
        // Vulnerable to brute force attacks
        
        if (Auth::attempt($request->only('email', 'password'))) {
            return response()->json(['message' => 'Login successful']);
        }
        
        return response()->json(['message' => 'Invalid credentials'], 401);
    }
}

🔐 API Security

✅ Good Example

php
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class ApiSecurityMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        // Add security headers
        $response = $next($request);
        
        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('X-Frame-Options', 'DENY');
        $response->headers->set('X-XSS-Protection', '1; mode=block');
        $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
        $response->headers->set('Content-Security-Policy', "default-src 'self'");
        
        return $response;
    }
}

❌ Bad Example

php
<?php

// No security headers
class ApiController extends Controller
{
    public function index()
    {
        // No security headers
        // Vulnerable to various attacks
        
        return response()->json(['data' => 'sensitive data']);
    }
}

📋 Security Checklist

✅ Do's

  • Validate All Input - Use form requests and validation rules
  • Use Strong Passwords - Enforce complex password requirements
  • Implement Authorization - Use policies and gates
  • Protect Sensitive Data - Use hidden fields and proper casts
  • Enable CSRF Protection - Protect against CSRF attacks
  • Implement Rate Limiting - Prevent brute force attacks
  • Use HTTPS - Encrypt data in transit
  • Add Security Headers - Protect against common attacks
  • Keep Dependencies Updated - Regular security updates
  • Log Security Events - Monitor for suspicious activity

❌ Don'ts

  • Don't trust user input - Always validate and sanitize
  • Don't store passwords in plain text - Always hash passwords
  • Don't expose sensitive data - Use hidden fields
  • Don't disable CSRF protection - Keep it enabled
  • Don't ignore rate limiting - Implement proper limits
  • Don't use weak encryption - Use strong algorithms
  • Don't skip security headers - Add all necessary headers
  • Don't ignore security updates - Keep everything updated

🔒 Security First: Security should be considered from the beginning of development, not as an afterthought. Regular security audits and penetration testing are essential.

Built with VitePress