In this blog we describe a few easy security mistakes and how to fix them.
Contents hide
1. Not Using HTTPS
- Mistake: Serving your Laravel application over HTTP instead of HTTPS leaves it vulnerable to man-in-the-middle (MITM) attacks where sensitive data can be intercepted.
- Solution: Always use HTTPS in production environments by setting up SSL certificates and forcing HTTPS by adding this to the($this->app->environment(‘production’)) { \URL::forceScheme(‘https’); }
2. Using Weak Encryption Keys
- Mistake: Failing to set a strong
APP_KEY
in the.env
file, which is crucial for encryption, session security, and password hashing. - Solution: Set a strong application key using the following command:bash
php artisan key:generate
Ensure this key remains secret and never shared publicly.
3. Not Validating Input Data
- Mistake: Skipping input validation can open your application to various attacks such as SQL injection, cross-site scripting (XSS), and other vulnerabilities.
- Solution: Use Laravel’s built-in validation to ensure that all input data is sanitized and follows the expected format.php
$request->validate([ 'email' => 'required|email', 'password' => 'required|min:8', ]);
4. Storing Sensitive Data in the Codebase
- Mistake: Hardcoding sensitive data such as API keys, database credentials, or private tokens directly in the codebase can lead to security breaches if the code is shared or exposed.
- Solution: Store sensitive information in environment variables (
.env
) and use Laravel’s configuration system to load them. Never commit the.env
file to version control.
5. Improper File Permissions
- Mistake: Allowing write permissions on sensitive files, such as the
.env
file, can lead to unauthorized users gaining access to configuration data. - Solution: Set proper file permissions for your Laravel project. Generally, you want directories like
storage
andbootstrap/cache
to be writable by the web server, but other files should not be writable.
6. Not Escaping User-Generated Content
- Mistake: Failing to escape user-generated data before rendering it on the page can lead to XSS attacks.
- Solution: Use Laravel’s built-in templating engine, Blade, which automatically escapes output. If you need to disable escaping (which is rare), ensure the content is properly sanitized.
{{ $userInput }} // Escaped
{!! $userInput !!} // Unescaped (Be careful)
7. Not Limiting Mass Assignment
- Mistake: Allowing mass assignment on models without properly controlling which fields are fillable can let attackers modify unintended fields.
- Solution: Use the
$fillable
or$guarded
properties in your Eloquent models to specify which fields can be mass-assigned.phpprotected $fillable = ['name', 'email'];
8. Exposing Debug Mode in Production
- Mistake: Leaving
APP_DEBUG=true
in the.env
file in production environments can expose sensitive information such as stack traces, database credentials, and API keys in error messages. - Solution: Always set
APP_DEBUG=false
in production, and consider using error logging to capture and safely store errors without revealing them publicly.
9. Insecure File Upload Handling
- Mistake: Not properly validating and sanitizing uploaded files can lead to malicious file uploads, which can execute server-side code.
- Solution: Use Laravel’s file upload validation rules to check the type, size, and other attributes of uploaded files.php
$request->validate([ 'file' => 'required|file|mimes:jpg,png,pdf|max:2048', ]);
Store the files outside of the public directory and use secure methods to access them.
10. Failing to Implement Rate Limiting
- Mistake: Not implementing rate limiting on sensitive routes like login or API endpoints can expose your application to brute force attacks or denial-of-service (DoS) attacks.
- Solution: Use Laravel’s built-in rate limiting middleware to protect your routes.php
Route::middleware('throttle:10,1')->group(function () { Route::post('/login', 'AuthController@login'); });
11. Unnecessary Exposure of Sensitive Endpoints
- Mistake: Leaving sensitive routes like
/telescope
,/horizon
, or/admin
accessible to the public can expose valuable debugging or administrative tools. - Solution: Protect these routes by restricting access via middleware or IP whitelisting. For example, limit access to Laravel Telescope to local environments:php
if (app()->environment('local')) { Telescope::auth(function () { return true; }); }
12. Outdated Laravel and Dependencies
- Mistake: Running outdated versions of Laravel or its dependencies can leave known vulnerabilities unpatched.
- Solution: Regularly update Laravel and its dependencies by running
composer update
, and monitor security advisories for packages you use.
13. Not Using CSRF Protection
- Mistake: Failing to implement Cross-Site Request Forgery (CSRF) protection can allow attackers to trick users into performing actions on their behalf.
- Solution: Laravel includes CSRF protection by default for all POST, PUT, PATCH, and DELETE requests using the
@csrf
directive in forms.blade<form method="POST" action="/submit"> @csrf <button type="submit">Submit</button> </form>
14. Not Using Secure Password Hashing
- Mistake: Storing passwords in plain text or using weak hashing algorithms makes your application vulnerable if the database is compromised.
- Solution: Always use Laravel’s
bcrypt
(orargon2
for stronger security) for hashing passwords:phpuse Illuminate\Support\Facades\Hash; $hashedPassword = Hash::make($password);
15. Ignoring Logs and Alerts
- Mistake: Failing to monitor application logs for suspicious activities or errors can lead to missed security incidents.
- Solution: Regularly check Laravel logs (
storage/logs
) and set up alerts for critical events. You can integrate Laravel with third-party monitoring tools like Sentry or New Relic for real-time monitoring.
16. Updating with $request->all()
A bit similar like point 7.
Although, it seems really convenient, when you update a user like this:
$user->update($request->all());
There is a security risk. For example, if you have a column this_is_an_admin=1, a user can set this variable via the browser’s Developer Console.
It would be better to only filter the right $request variable with:
$request->only(['name', 'newsletter_subscribed']);
Got any other security points? Let us know in the comments!