Laravel Security Checklist for Heroku

Laravel has robust security features built-in - configure them properly for Heroku deployment.

Expedited WAF for Laravel

Expedited WAF adds network level request filtering, preventing malicious attacks from ever touching your application. This enables true defense in depth for your Laravel app—even if your framework has a vulnerability or is improperly configured, Expedited WAF dynamically blocks threats before they reach your code.

From experience, we’ve seen many applications where attacks technically didn’t succeed, but burned through so many server resources that the application went down anyway. By filtering malicious requests at the network edge, Expedited WAF protects both your security and your availability.

Expedited WAF protects your Laravel app on Heroku

New Security Features for Your Laravel App

These capabilities aren’t available out of the box with Laravel, but Expedited WAF adds them instantly:

Laravel Core Security

  • Generate and protect APP_KEY

    php artisan key:generate
    heroku config:set APP_KEY='base64:...'
    
    • Never commit to version control
    • Rotate periodically
    • Used for encryption and signed cookies
  • Set APP_DEBUG to false in production

    heroku config:set APP_DEBUG=false
    heroku config:set APP_ENV=production
    
    • Never expose stack traces
    • Disable detailed error messages
  • Configure trusted proxies

    // app/Http/Middleware/TrustProxies.php
    protected $proxies = '*';  // Heroku uses proxy
    protected $headers = Request::HEADER_X_FORWARDED_FOR |
                        Request::HEADER_X_FORWARDED_HOST |
                        Request::HEADER_X_FORWARDED_PORT |
                        Request::HEADER_X_FORWARDED_PROTO;
    
  • Enable HTTPS enforcement

    // app/Providers/AppServiceProvider.php
    public function boot()
    {
        if ($this->app->environment('production')) {
            URL::forceScheme('https');
        }
    }
    
  • Configure session security

    // config/session.php
    'secure' => env('SESSION_SECURE_COOKIE', true),
    'http_only' => true,
    'same_site' => 'lax',
    

CSRF Protection

  • Verify CSRF middleware is enabled

    // app/Http/Kernel.php - should be in $middlewareGroups['web']
    \App\Http\Middleware\VerifyCsrfToken::class,
    
  • Use @csrf directive in forms

    <form method="POST">
        @csrf
        <!-- form fields -->
    </form>
    
  • Configure CSRF exceptions carefully

    // app/Http/Middleware/VerifyCsrfToken.php
    protected $except = [
        // Only add if absolutely necessary
    ];
    

Database Security

  • Configure Heroku Postgres

    // config/database.php
    'pgsql' => [
        'driver' => 'pgsql',
        'url' => env('DATABASE_URL'),
        'host' => env('DB_HOST', '127.0.0.1'),
        'port' => env('DB_PORT', '5432'),
        'database' => env('DB_DATABASE', 'forge'),
        'username' => env('DB_USERNAME', 'forge'),
        'password' => env('DB_PASSWORD', ''),
        'charset' => 'utf8',
        'prefix' => '',
        'schema' => 'public',
        'sslmode' => 'require',  // Important for Heroku
    ],
    
  • Use Eloquent ORM and Query Builder

    • Always use parameterized queries
    • Never use DB::raw() with user input
    // Good
    User::where('email', $email)->first();
    
    // Bad
    DB::select("SELECT * FROM users WHERE email = '$email'");
    
  • Enable query logging carefully

    // Only in development
    if (config('app.debug')) {
        DB::enableQueryLog();
    }
    

Authentication & Authorization

  • Use Laravel Breeze/Jetstream

    • Don’t roll your own authentication
    • Use maintained authentication scaffolding
  • Configure password validation

    // config/fortify.php or validation rules
    'password' => ['required', 'string', 'min:12', 'confirmed', Rules\Password::defaults()],
    
  • Implement rate limiting

    // routes/web.php
    Route::middleware('throttle:5,1')->group(function () {
        Route::post('/login', [AuthController::class, 'login']);
    });
    
    // app/Http/Kernel.php
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    
  • Use Laravel Gates and Policies

    // app/Policies/PostPolicy.php
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id;
    }
    
    // In controller
    $this->authorize('update', $post);
    
  • Protect routes with middleware

    Route::middleware(['auth'])->group(function () {
        Route::get('/dashboard', [DashboardController::class, 'index']);
    });
    

File Uploads & Storage

  • Configure S3 for file storage

    // config/filesystems.php
    's3' => [
        'driver' => 's3',
        'key' => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION'),
        'bucket' => env('AWS_BUCKET'),
    ],
    
    // .env
    FILESYSTEM_DRIVER=s3
    
  • Validate file uploads

    $request->validate([
        'avatar' => 'required|image|mimes:jpeg,png,jpg|max:2048',
    ]);
    
  • Sanitize filenames

    $filename = Str::random(40) . '.' . $file->extension();
    

Security Headers

  • Add security headers middleware

    // app/Http/Middleware/SecurityHeaders.php
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        $response->headers->set('X-Frame-Options', 'DENY');
        $response->headers->set('X-Content-Type-Options', 'nosniff');
        $response->headers->set('X-XSS-Protection', '1; mode=block');
        $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
        return $response;
    }
    
  • Configure Content Security Policy

    • Use spatie/laravel-csp package
    composer require spatie/laravel-csp
    php artisan vendor:publish --provider="Spatie\Csp\CspServiceProvider"
    

Mass Assignment Protection

  • Use $fillable or $guarded

    // app/Models/User.php
    protected $fillable = ['name', 'email', 'password'];
    // OR
    protected $guarded = ['id', 'is_admin'];
    
  • Never use $guarded = []

  • Validate all input

    $validated = $request->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users',
    ]);
    

XSS Prevention

  • Use Blade templating

    • Blade auto-escapes by default with {{ }}
    • Only use {!! !!} when absolutely necessary
  • Sanitize HTML input

    composer require mews/purifier
    
    $clean = Purifier::clean($dirty);
    

API Security

  • Use Laravel Sanctum for APIs

    composer require laravel/sanctum
    php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
    
  • Implement API rate limiting

    // routes/api.php
    Route::middleware('throttle:60,1')->group(function () {
        Route::get('/user', function (Request $request) {
            return $request->user();
        });
    });
    
  • Configure CORS properly

    // config/cors.php
    'paths' => ['api/*'],
    'allowed_origins' => [env('FRONTEND_URL')],
    'allowed_methods' => ['GET', 'POST', 'PUT', 'DELETE'],
    'allowed_headers' => ['Content-Type', 'Authorization'],
    

Environment & Secrets

  • Store secrets in Heroku config

    heroku config:set APP_KEY='...'
    heroku config:set DATABASE_URL='...'
    heroku config:set AWS_ACCESS_KEY_ID='...'
    
  • Never commit .env file

    • Add to .gitignore
    • Use .env.example for template
  • Validate environment configuration

    // config/app.php
    'key' => env('APP_KEY'),  // Will fail if not set
    

Logging & Monitoring

  • Configure production logging

    // config/logging.php
    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['daily'],
        ],
    ],
    
  • Don’t log sensitive data

    • Passwords, tokens, API keys
    • Credit card information
    • Personal data (GDPR)
  • Set up error tracking

    • Use Sentry, Bugsnag, or Flare
    composer require sentry/sentry-laravel
    

Dependency Management

  • Keep Laravel and packages updated

    composer update
    composer audit  # Laravel 9+
    
  • Use security checker

    composer require --dev enlightn/security-checker
    php artisan security-check
    
  • Pin dependency versions

    • Use composer.lock
    • Review updates before deploying

Heroku Deployment

  • Create Procfile

    web: vendor/bin/heroku-php-apache2 public/
    
  • Configure buildpack

    heroku buildpacks:set heroku/php
    
  • Run migrations on deploy

    heroku run php artisan migrate --force
    
  • Configure app for Heroku

    // config/trustedproxy.php
    protected $proxies = '*';
    

Testing & Deployment

  • Run Laravel security checks

    composer require enlightn/enlightn --dev
    php artisan enlightn
    
  • Test on Heroku staging

    heroku create myapp-staging
    git push staging main
    
  • Write security tests

    public function test_csrf_protection()
    {
        $response = $this->post('/login');
        $response->assertStatus(419);  // CSRF error
    }
    

Pre-Deployment Checklist

  • APP_DEBUG=false
  • APP_ENV=production
  • All secrets in Heroku config
  • SSL enforced
  • Security headers configured
  • File storage on S3
  • Database uses SSL
  • Rate limiting enabled
  • CSRF protection active

Additional Resources