In the recent Laracon AU 2024 Conference, Stephen Rees-Carter explained the biggest mistakes developers do while (and after) building a website using Laravel. Stephen is a great friendly hacker and has ethically hacked many websites and he mentioned all the stuff that we as developer do wrong which make people like him easily penetrate our website and steal sensitive data.
In this blog, we will go through the most well-known vulnerability found in Laravel applications which Stephen finds a lot in the Laravel applications. Some of them will be very simple yet very important that I personally miss them sometimes.
Outdated Libraries & Packages
We tend to use libraries to make our life easier and there is nothing wrong with that, in fact, that’s better cause developer more intelligent than us are working on these libraries and making the process less painful. And these guys don’t want their libraries to be vulnerable and they keep fixing it almost daily and whenever they update the libraries, we are suppose to update them. But there is one problem, how do we know when to update or when there is a new update. Well, the package managers are not just to install the libraries & packages but also to keep you updated with the new updates from libraries.
For PHP Libraries, we have composer, a really great PHP dependency manager, and I don’t need to explain to you what it does. But in case you want to check for any new updates in your libraries, you can run the following command:
composer outdated
This will tell you all the outdated libraries in your application, and trust me if you run this command once a week, there will always be at least one package that is outdated. I just ran this on a project I started a very few weeks ago, and there are tons of packages that needs to be updated. But not all updates means that the current one is vulnerable and to find any vulnerable packages there is another command that you can run for composer:
composer audit
This command to look for the known vulnerabilities in your dependencies list, you might find some scary one out there and some of them might be even unrelated to you or does not make sense but if it’s there than it’s a red flag and you should immediately update those packages.
And all you need to run to fix all these issues is a fairly simple command, just ran the following command and it will do the magic:
composer update
And yeah as I said, it’s a very simple thing yet very dangerous if you don’t commit to it.
Use Secured Session Cookies
This is again a very simple thing yet very important and if you are not using the latest Laravel version then it’s even scarier if you have mis-configured your environment.
The environmental variable we are talking about here is the SESSION_SECURE_COOKIE
. You can find this environmental variable being used in the config/session.php file and yes there is no default value for this (at least for my project) and it does not even exists in my .env file, so a big mistake there. The first thing you should do right now (specially if you are not using the latest Laravel version) is to add a default value to this config and make sure it’s true.
But why do we need to do that particularly? The issue is that when one of your users access the website on HTTP or even HTTPS (with SESSION_SECURE_COOKIE
as false) then the cookie isn’t secure or encrypted which make your user and your website vulnerable to cookie hijacking and it’s a nightmare so setting that to true will make sure that your cookies are encrypted and secure.
But there is a good news with it, with the latest verison of Laravel if this value is not set and has no default value then Laravel will automatically use SESSION_SECURE_COOKIE
for HTTPS requests. Cherry on top will be to just redirect HTTP request to HTTPS request.
HSTS Encryption
Do you know about MITM (man-in-the-middle) attack? It’s very dangerous and almost undetectable for a normal user and the hacker can send a insecure HTTP request to the user even though the user thinks it’s a HTTPS request. Now that’s messed up! And we want to do everything to avoid that, that’s where HSTS kicks in.
There is a header called Strick-Transport-Security
which can help us with this problem, basically once your user hits the server, the browser will remember it and will always redirect your users to HTTPS (through an internal redirect) even though the MITM sends you a HTTP request.
And to do this you just need to send a header with all of your request (preferably through a middleware) and here it follows:
public function handle(Request $request, Closure $next): Response
{
$res = $next($request);
$res->headers->set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
return $res;
}
To learn even more about HSTS and the preloads, I recommend to checkout this HSTS Preload website. HSTS Preload is like a cached urls for the browser to know that all these require HSTS by default.
Use The Brackets Wisely
We all have at least once used this sentence in our blade files {!! $var !!}
and ignore the escaping. I’m pretty sure that most of the developers reading this already know about this and the consequences with it but there are some times when we do stupid mistakes and we will discuss 2 cases where you can improve your security.
HTML in Controllers
Now this is interesting because me personally have done this mistake over the years (pre-livewire) where you have a situation to write some HTML in your controllers or logic scripts. I personally used it places like where you want to always display a verified badge next to a name and you don’t want to repeat all that codes over and over again or even miss it. We do have components now a days that can help but still there are other cases we need to write HTML in the logic which sucks but we are lazy. And since we are writing these HTML codes in the backend, we tend to blindly trust these codes which is horrible because remember it’s not static, it got some values from db in it. So let’s see how we can fix that.
Here I have an example of a script that adds the italic verified text on the user name.
public function getNameDisplayAttribute(): string
{
return $this->name . " <em>(Verified)</em>";
}
And we will probably use it in our blade like this:
{!! $user->name_display !!}
Some developers might find this looking fine but it’s actually a time bomb ready to go off anytime. What’s wrong here is that we are not escaping the name value here although we don’t really need to do that on the blade file but since our HTML codes are now in the “backyard” we must escape it. And the way to it is very easily, you just wrap the variable in the Laravel’s e
function:
public function getNameDisplayAttribute(): string
{
return e($this->name) . " <em>(Verified)</em>";
}
And that’s it!
Markdowns
Using markdowns for descriptions? It gotta be safe. Here’s an example of a typical markdown rendering in blade:
{{ Str::of($description)->markdown() }}
Seems harmless yet quite the opposite. There are two main options you can add to your markdown function to make it harmless which are html_input
to be escape
and allow_unsafe_links
to be false
.
{{ Str::of($description)->markdown(['html_input' => 'escape', 'allow_unsafe_links' => false]) }}
And that’s it for the markdown, pretty simple.
Don’t Trust Codes You Didn’t Wrote (Third-Party)
Let’s take a step back and get back to where this blog started with, libraries and packages but this time third-party packages. We love to make things easier, but we must do it the right way. This section will focus on how to correctly integrate any third-party plugin you are getting (even the ones that are very popular) via a CDN.
It’s very simple and straight-forward, you just need to add an integrity hash into the script/link tag. Basically, this is an assurance that your are not getting codes that has been modified since the the new file content won’t match the hash and the browser will block it, making sure you are not getting Cross-Site Scripting Attacks.
Here’s an example of what normal tags without the integrity hash looks like (JQuery in this example):
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
And here’s one with the integrity hash:
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
Most of the CDN does come with the integrity hash such as JQuery, Bootstrap etc. but if the one you’re planning to use or are already using doesn’t have it by default then you can generate one by yourself using a hash generator.
Reminder: Don’t trust any package that does not have enough popularity (can be highly vulnerable and volatile)
And that brings us to the end of this article! I recommend watching the video where Stephen is explaining exactly what I mentioned here but in a more interesting way: