DEV Community

Fongoh Martin T.
Fongoh Martin T.

Posted on • Edited on

Laravel 11 Security Audit Guide (Part 2 of 3)

Laravel 11 Security Audit Guide (Part 2 of 3)

Welcome back to the second part of our Laravel 11 security audit guide. In Part 1, we tackled brute force attacks, insecure direct object references, SQL injections, and cross-site scripting. Now let’s go deeper. Part 2 focuses on four more issues that can compromise your Laravel app: CSRF, insecure file uploads, broken authentication, and missing rate limiting on APIs.

5. Cross-Site Request Forgery (CSRF)

The Problem

Cross-Site Request Forgery (CSRF) is an attack that tricks a logged-in user into making an unwanted request on a web application. Imagine a logged-in admin unknowingly clicking on a malicious link or loading an image that sends a POST request to /users/delete/12. If your Laravel app doesn’t have CSRF protection, this request could succeed.

Laravel is actually strong out of the box here, but vulnerabilities happen when developers disable or forget to use CSRF tokens, especially when creating custom forms or JavaScript-driven frontend logic.

How to Test

  • Login to your Laravel app as a regular user.
  • Create a malicious HTML form like this:
<form action="https://yourapp.com/account/delete" method="POST">
  <input type="submit" value="Click here">
</form>
Enter fullscreen mode Exit fullscreen mode
  • Open the page in another tab where the user is still logged in.
  • Click the form. If the user’s account is deleted or changed without a CSRF token, you’re vulnerable.

How to Fix It

Always use Laravel’s @csrf Blade directive in forms.

<form method="POST" action="/account/delete">
  @csrf
  <button type="submit">Delete</button>
</form>
Enter fullscreen mode Exit fullscreen mode

In JavaScript-driven apps, ensure you send the CSRF token in the headers:

axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
Enter fullscreen mode Exit fullscreen mode

Laravel will verify this via the VerifyCsrfToken middleware.

Additional Advice

  • Never disable CSRF middleware unless absolutely necessary.
  • Use CSRF tokens in all state-changing requests (POST, PUT, DELETE).
  • Educate your team about how CSRF works and what mistakes open the door to it.

6. Malicious File Uploads

The Problem

Allowing users to upload files is a major attack surface. A user could upload a PHP shell script or a crafted image file that contains hidden JavaScript. If the upload directory is publicly accessible and you don't validate file types or contents, attackers could execute malicious code or serve infected files to other users.

How to Test

  • Find a file upload form (like profile picture or document upload).
  • Upload a .php, .svg, or .html file disguised as an image.
  • Try to access the file from the browser after upload. If it renders or executes, you’re at risk.

The Fix

  • Restrict allowed file types strictly using validation rules:
$request->validate([
  'avatar' => 'required|mimes:jpg,jpeg,png|max:2048'
]);
Enter fullscreen mode Exit fullscreen mode
  • Store files using Laravel’s Storage::put() outside of the public directory:
Storage::disk('local')->put('avatars/user123.jpg', $uploadedFile);
Enter fullscreen mode Exit fullscreen mode
  • Never allow .php, .html, .svg, or any executable files.

Additional Advice

  • Scan files using a malware scanner.
  • Rename files using random hashes to avoid filename-based attacks.
  • If files must be served to the public, proxy them through a controller that checks permissions and MIME types.

7. Broken Authentication and Token Theft

The Problem

In a modern Laravel app, especially one with APIs, you might be using tokens like JWT or Laravel Sanctum for authentication. If tokens are not securely generated, stored, or transmitted, attackers can hijack sessions or impersonate users.

Tokens leaked via logs, URLs, browser storage, or unsecured APIs can be replayed from anywhere. If your token doesn’t expire or isn’t tied to a device or IP, the attacker has full access.

How to Test

  • Intercept a login request using a proxy tool like Burp Suite.
  • Extract the authentication token.
  • Replay the token using a different IP/device.
  • If the session is still valid, it means token binding is weak or missing.

The Fix

  • Always use HTTPS to encrypt token transmission.
  • Prefer HTTP-only cookies (like with Laravel Sanctum) for web auth.
  • Implement token expiration and refresh logic:
// Sanctum config: config/sanctum.php
'expiration' => 60, // minutes
Enter fullscreen mode Exit fullscreen mode
  • Consider logging out sessions on token theft detection.

Additional Advice

  • Never store tokens in localStorage or URLs.
  • Log unusual token usage: IP changes, user-agent changes, or geographic anomalies.
  • Invalidate tokens on password changes or suspicious login patterns.

8. Missing API Rate Limiting

The Problem

Without proper rate limiting, any API endpoint is vulnerable to abuse. Attackers can flood your server with traffic, perform credential stuffing, or brute force endpoints, causing high server load or unexpected behavior.

Laravel provides a great rate limiting system, but many developers forget to use it on custom API routes.

How to Test

  • Identify an unauthenticated or public API endpoint.
  • Write a script or use a tool like Apache Bench to send 1000+ requests in a short time.
  • Observe if responses slow down, fail, or get accepted without delay.

The Fix

Apply Laravel’s throttle middleware to your API routes:

Route::middleware('throttle:60,1')->group(function () {
    Route::get('/public-data', [ApiController::class, 'index']);
});
Enter fullscreen mode Exit fullscreen mode

This example allows 60 requests per minute.

For more control, define custom rate limiters in RouteServiceProvider:

RateLimiter::for('custom-api', function (Request $request) {
    return Limit::perMinute(30)->by($request->ip());
});
Enter fullscreen mode Exit fullscreen mode

Additional Advice

  • Log all requests and monitor IPs with high frequency.
  • Use rate limiting even for authenticated users to prevent abuse.
  • Combine with CAPTCHA and temporary IP bans where needed.

Part 3 will cover error disclosure, security headers, access control flaws, and business logic abuse. These are often the final cracks that let attackers in.

If this helped you improve your app’s security posture, let’s connect in the comments. I’d love to hear how others are securing their Laravel apps in production.

Top comments (0)