Security Tip: The Trap That Caught Me!
[Security Tip #135] During a recent pentest, I suddenly got hit with 403s on every request. It wasn't a WAF - it was a clever little trap one of my clients built to catch anyone poking at Livewire. Let me show you how it works. 🤓
One of the cool parts of doing Laravel Security Audits and Penetration Tests is seeing how my clients go about solving different problems in their code, and some of their creative security implementations. One such example is a class called LivewireBotBanner, which I came across during a recent audit.
I discovered it by accident when I was attempting to override some Livewire properties that were incorrectly configured. I made a request to a locked property and suddenly hit 403 errors on all requests. I assumed it was a WAF being overly twitchy, rotated my IP address, and got back in. But it didn't feel right, my request shouldn't have looked suspicious - especially not compared to some of the other requests I'd made.
So I went digging...
I quickly discovered a global middleware called EnforceBotProtection which looks for suspicious User-Agents (i.e. curl, wget, python, etc), and that made me smile, but clearly wasn't the cause of my 403 - Burp's browser (which I use during pentests) isn't that lazy.
I kept digging and saw this line in the middleware:
if (LivewireBotBanner::isBanned($ip)) {
abort(403, 'Forbidden');
}After digging into LivewireBotBanner, I realised it was a class dedicated to identifying and blocking anyone making suspicious requests to Livewire. The global exception handler (\App\Exceptions\Handler) was configured to catch two notable Livewire exceptions, and call LivewireBotBanner::ban() to do the actual banning:
if (
$e instanceof ComponentNotFoundException
|| $e instanceof CannotUpdateLockedPropertyException
) {
LivewireBotBanner::ban($request, $e);
}Such a simple and elegant trick: ComponentNotFoundException is fired when a component that doesn't exist is requested, and CannotUpdateLockedPropertyException is fired when the user attempts to change a locked property. Neither of these should happen during normal operation, so they provide strong indications that the user is doing something suspicious.
The automatic response to these exceptions was to block the user by IP address, returning a 403 Forbidden, which feels like a suitable response when you're confident it will only be hit by someone doing something intentionally suspicious.
That said, there are a couple of caveats I should mention:
- This system blocks by IP address, and it's trivial to rotate your IP if you know what you're doing. In other words, it won't stop a determined attacker. However, you could enhance this protection by introducing a temporary ban on the user account itself - lock them out for 15 minutes, or send an email with an unlock link. The latter serves a dual purpose of notifying the user of suspicious activity, in case of account takeover. (In defence of my client, they have public Livewire forms where IP is the only unique identifier available.)
- False positives are possible. A buggy deploy or misconfigured properties could trigger these exceptions, locking out the user. So ensure you've got suitable tests and QA, and an unlock method.
- Couple this with logging and alerting, so your team are notified if users start getting blocked. This serves as both a warning system for an active attack, and an alert if something breaks and legitimate requests are blocked.
My recommendation is to explore something like this in your code. When you've got exceptions and errors that should only occur during suspicious activity, having some alerting or active defences in place surrounding these won't cost you much, but may slow down or prevent an attacker from poking at your app further.
Found this security tip useful? 👍
Subscribe now to get weekly Security Tips straight to your inbox - practical, actionable advice to help you build safer apps.
Want to go deeper? 🤓
Upgrade to a Premium Subscription for exclusive monthly In Depth articles. Your support directly funds my security work in the Laravel community. 🥰
Need a second set of eyes on your code?
Book a Laravel Security Audit and Penetration Test - or, for something more focused, a lighter-weight Security Review.
Finally, connect with me on Twitter, Bluesky, phpc.social, and LinkedIn.