Security Tip: Test for Missing Authorisation

[Tip#48] We write tests for everything else, so why not write tests for authorisation as well?

Security Tip: Test for Missing Authorisation

Greetings friends! The second most common security issue I discover when doing security audits on Laravel apps is… Missing Authorisation! I was honestly surprised to see it so high - Laravel has many robust and well-known authorisation tools, but the numbers didn’t lie. As I dug into it, I realised it most commonly occurs on a single route, due to a single `authorize()` call being missed in a controller action. I’ve covered many of Laravel’s authorisation features before, but for Today’s tip we’re focusing on a very specific idea that will help you avoid this exact problem.

Here’s our current Top 10:
#10 → Insufficient Input Validation
#9 → Missing Subresource Integrity (SRI)
#8 → Insufficient Rate Limiting
#7 → Cross-Site Scripting (XSS)
#6 → Outdated & Vulnerable Dependencies
#5 → Insecure Function Use
#4 → Missing Security Headers
#3 → Missing Content Security Policy (CSP)
#2 → Missing Authorisation

🛡️ When was your last annual security audit?
🕵️ Book a security audit now!

Looking to learn more?
OWASP Security Tip: A06:2021 – Vulnerable and Outdated Components
▶️ OWASP In Depth: A05:2021 – Security Misconfiguration


Test for Missing Authorisation

In my experience auditing Laravel apps, the most common cause for missing authorisation is due to the developer forgetting to include an `authorize()` call within a controller action. It’s far less common to forget when authorisation is handled within the routes file1, but it can be easily missed in there too, so I’m talking to everyone here!

Forgetting authorisation is super easy to do - you’re adding your route, writing your controller action, thinking about the logic, where the inputs go, the validation rules you need, which jobs and events are dispatched, and what is returned to the browser. But somewhere in there you’ve forgotten to check if the user has permissions to actually do any of it…

So how do we avoid this trap? Write tests that specifically check for permissions, alongside your feature and unit tests!

If you think about it, it makes perfect sense. We write tests that cover the success and failure states of our features - ensuring everything works as expected, and nothing is broken accidently in the future. So we really should be writing tests that check we’ve implemented authorisation correctly, and that it doesn’t break in the future.

When I’m writing tests, I try to hit each of these use cases2:

  • Guest request
  • Not allowed user request
  • Allowed user request

I typically use one of these assertions in my tests3:

$this->assertStatus($status);
$this->assertUnauthorized(); // 401
$this->assertForbidden();    // 403
$this->assertNotFound();     // 404
$this->assertRedirect($url);

Once you get in the habit of writing these tests, you’ll start automatically writing them when adding new routes, making it harder to forget authorisation. You’ll also find you consider authorisation and permissions more, potentially avoiding assigning the wrong permissions or leaving access too open on sensitive routes.

Authorisation Resources

To learn more about Authorisation in Laravel, you can find all of the past Tips at: https://securinglaravel.com/t/authorization.

In Depth: Policy Objects

Stephen Rees-Carter • Apr 26, 2022

Greetings, my friends! After last month’s In Depth being delayed by due to COVID, it’s great to be back on schedule. I hope you found the reminder about what randomness functions to use last week helpful - and enjoyed the list of not-random methods I’ve encountered in the wild!

Read full story →

In Depth: Signed URLs

Stephen Rees-Carter • May 29, 2022

Greetings friends! I write to you today, a few days short of the 9 month anniversary of Laravel Security in Depth (it’s Tuesday), and my dashboard says 906 subscribers - which is just incredible. 🥰 Thank you so much for joining me on this journey! It looks like I’ve got 3 months to start planning something big for the 12 month anniversary!

Read full story →

In Depth: Insecure Direct Object References (IDOR)

Stephen Rees-Carter • Aug 1, 2022

Greetings friends! I hope you enjoyed last week’s Security Tip about Leaking Model Existence, and are thinking about areas in your apps that could benefit from adding another layer of security in that area.

Read full story →

Security Tip: Policy Filters

Stephen Rees-Carter • Sep 21, 2021

Greetings, I hope you learnt a lot from the In Depth email about Encryption last week. This week we’re moving onto something completely different: Policy Filters. 😁 Policy Filters may be familiar to …

Read full story →


  1. This is why I always recommend doing authentication and authorisation within your routes file. You’re far more likely to notice missing auth* when you’re looking at all of your routes in a single place than you are when you’re looking at your controller logic across multiple methods and files.

  2. Always ensure you’ve created all of the models you need for a successful request, otherwise a 404 due to route model binding may provide a false negative in your lacking authorisation tests.

  3. The full list can be found at: https://laravel.com/docs/10.x/http-tests#available-assertions