In this blog post, we're gonna take a closer look at some common Laravel security mistakes that I've encountered in projects, personally made or even came across on Stack Overflow approved answers. We'll briefly talk about file validation, mass assignment, and Laravel’s query builder. The goal of this blog is to demonstrate how small, easy to avoid mistakes can have a big impact on your application’s security. And to hopefully prevent you from making them in the future. So, let’s get started!

File validation

Laravel offers a robust and secure file system that enables developers to store and retrieve files from either local or cloud storage. Below is some code, straight from Stackoverflow, to handle a user uploading an avatar to their profile:

Can you spot how an attacker could abuse the following snippet?

public function storeImage(Request $request)
{
	$request->validate([
		'image' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
	]);

	$imageName = Str::random(16) . '.' . $request->image->getClientOriginalExtension();

	Storage::disk('public')->putFileAs("avatars", $request->image, $imageName);

	return back()->with('success','You have successfully uploaded your avatar!');
}

Say, an attacker wanted to upload a .html file with an XSS (Cross-site scripting) payload to your server. You’d expect Laravel’s server-side validation image and mimes:jpeg,png,jpg,gif,svg to take care of this.

Sidenote: This isn’t a bug in Laravel but simply how mime type checking works.

The crucial mistake being made is using Laravel’s getClientOriginalExtension method to retrieve the file’s extension directly from the request. Instead, in this scenario we want to use:

$request->image->extension();

This will guess the file’s mime type based on the contents of the actual file rather than what’s being received from the client.

Another safe way to handle this is to use Laravel’s put method to store files:

Storage::disk('public')->put(Str::random(16), $request->image);

This will automatically replace your filename with a random hash and append the correct extension.
The key takeaway here is to never trust user input, especially when it comes to file uploads.

 

 

A registration form for a user

Mass assignment

Can you spot the security flaw in this code?

User model

class User extends Model {

    protected $fillable = ['username', 'email', 'password', 'role'];

}

Register controller

class RegisterController
{
  public function create(Request $request) 
  {
    $request->validate([
      'username' => 'required|string',
      'email' => 'required|email|unique:users',
      'password' => 'required|string|min:12|confirmed',
    ]);

    $user = new User();
    $user->role = 'guest'
    $user->fill($request->all());
    $user->save();

    return response()->json(['success' => true],201);
  }
}

If a malicious user were to forge the register request and add "role": "admin" to the form payload. Laravel would happily grant this user access to your entire system.

This is due to $request→all() not only taking validated data from the request but ALL data. In combination with role being added to the $fillable property of the model, the user would be created with the role "admin".

Prevent this by either using $request→validated() to only retrieve validated input from your request. Or ensure your fillable properties are set up correctly.

Bonus points if you spotted the password being stored in plain text ;)

Query builder parameter binding

SQL injection is a common attack vector for web applications, and Laravel is no exception. It occurs when an attacker is able to input malicious SQL code into a web application's input fields or query strings.

Here is an example of SQL injection using Laravel's query builder:

//User input
$search = "1; DROP TABLE users;";

DB::table('users')->whereRaw("name = " . $search)->get();

This code is vulnerable to SQL injection because it allows the attacker to execute arbitrary SQL code by setting the $search variable to a string that includes SQL commands. In this case, the attacker could delete the entire users table by setting $search to "1; DROP TABLE users;".

To prevent SQL injection attacks, always use Laravel's query builder or parameter binding when constructing SQL queries.

Example of parameter binding:

$search = "1; DROP TABLE users;";

DB::table('users')->whereRaw("name = ?", $search)->get();

Conclusion

Though the examples above might be quite obvious to some, I hope this article illustrates the importance of having a solid understanding of the inner workings of a framework, even when it appears to handle security seamlessly. Despite Laravel's beginner-friendly nature and minimized overhead for developers, don’t forget to remain cautious and avoid complacency.

Looking for Laravel experts? 

Author: Robbe Reygel
PHP developer

More insights

Cross-platform applicaties with React Native

Never before has developing native mobile applications been as accessible as it is today. At Codana, we do this by using the React Native, an open-source framework developed by Meta.

Author: Jinse Camps
Architect | Analyst
Jinse Camps
dev

Laracon EU 2024

A fantastic learning experience to inspire and be inspired together with a lot of other Laravel passionate people! Something we couldn't miss and very much connect with the community. What a top event! Who will we see next editions? 😮

Author: Noah Gillard
PHP / Laravel Developer
Noah Gillard AI generated Face
laracon codana persoon

An efficient tourism data management system

A TDMS or Tourist Data Management System, is simply a platform that retrieves data from various sources, processes it internally either automatically or not, and offers this data back to external platforms.

Author: Tom Van den Eynden
Web Architect | Coordinator
Tom Van den Eynden
laptop

Tourism Data Management Systems

In dit artikel verkennen we wat een TDMS is, waarom het essentieel is voor de toerisme-industrie, en hoe technologieën zoals Laravel en ElasticSearch het verschil kunnen maken. 

Author: Tom Van den Eynden
Web Architect | Coordinator
Tom Van den Eynden
tdms

The difference between data management and data processing in a digital economy

Gegevens zijn cruciaal voor bedrijven en het begrijpen van de verschillen tussen gegevensbeheer en gegevensverwerking kan verwarrend zijn. In dit artikel zullen we deze verschillen in de digitale economie nader bekijken om hun doelen en toepassingen beter te begrijpen.

Author: Tom Van den Eynden
Web Architect | Coordinator
Tom Van den Eynden
gegevensverwerking

Test Driven Development - application to a project

TDD, or in full Test Driven Development, is an approach to development where we start from writing tests.

Author: Sarah Jehin
PHP developer
Sarah Jehin
development