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>
- 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>
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');
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'
]);
- Store files using Laravel’s
Storage::put()
outside of the public directory:
Storage::disk('local')->put('avatars/user123.jpg', $uploadedFile);
- 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
- 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']);
});
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());
});
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)