Laravel Broken Access
Control Guide: Examples
and Prevention

stackhawk

StackHawk|November 9, 2021

Broken access control systems can wreak havoc on your application. It's best to invest in tools and make sure they are configured with your application.

Access control systems are a lynchpin of the modern Web. Identity management systems handle the question, "Who are you?" and  authentication systems handle the question, "Can you prove who you are?" That leaves the question, "What can you do?" This is answered by the access control system. Now, imagine the fallout if this system is compromised. 

In this post, we'll briefly look at what an access control system is and what it does. We'll also go through some examples of broken access control systems and how to improve them so they can better secure your application. Let's start by learning a little bit about access control systems.

What Is an Access Control System?

An access control system dictates the permissions that an authenticated user has on a given application. Whenever an authenticated user carries out an action like opening a file or viewing a report, the access control system checks whether the user has the necessary permissions to do so. Some of these systems may also have different granularities in the permissions they grant. For example, a user may view a file but not be able to edit it. Or they may be able to edit it, but not delete it.

Laravel Broken Access Control Guide: Examples and Prevention image

Overview of an access control system.

The image above depicts a high-level overview of what happens with an access control system: 

  • Authenticated users make requests to resources.

  • All of the different users are of the same type and have the same permissions.

  • According to the permissions list, these users can only read and edit the resource. They do not have the delete permission.

  • The two users carrying out valid actions on the record are allowed through without issue.

  • The user requesting to delete the record is shown an error because the user carried out an action that they're not allowed to do.

So now that you know what an access control system does, let's move on to our next question. 

What Is a Broken Access Control System?

A broken access control system is one in which your access control permissions are misconfigured or your access control system itself is misbehaving. Going back to our previous example, let's imagine the following: 

  • The records in the previous example were medical records.

  • Each authenticated user should only be able to view or edit their own medical records.

This means that your access control system should differentiate between individual records (or resources) if that's required by your use case. 

Sometimes you may only need to do a check on the type of resource. For example, if you have the "warehouse owner" role in an inventory management system, you should be able to see all of the purchase orders in the system, not just one. 

And sometimes, you may need the system to differentiate on an individual resource basis, as shown below.

Laravel Broken Access Control Guide: Examples and Prevention image

Overview of a broken ACL implementation.

In the above example of a medical record system, each user should only be able to see their own file (the resource). The access control system should differentiate between individual files and check permissions for the file itself. Allowing any authenticated user of the same type to access medical records without checking the specific file's permission is an example of a broken access control system. 

To carry this example further, only medical staff like doctors and nurses should have access to all patient files. Individual patients like Bob, Sara, and Mel should only have access to their own files. 

Laravel Broken Access Control Guide: Examples and Prevention image

Access Control and Laravel

Laravel supports many different access control models. I've listed some of the more common ones below. 

  • The very basic starter kits allow you to build simple authentication systems that answer the  "authenticated?"  question with a "yes/no" answer.

  • You also have Gates, which lets you bind additional authentication to methods on your controller.

  • Policies are a more robust way to write authorization logic and center it around a particular model or resource.

  • For more advanced use cases, take a look at Laravel Passport and Laravel Sanctum. Passport is built on top of the robust Oauth2 specification, and Sanctum is a great choice if you want to provide authorization capability based on tokens for your single-page app.

Make sure you understand the pros and cons of each system and pick something that works well for you. 

Now let's look at some common mistakes that people do when rolling out an access control system—and how to fix them. 

Insecure Direct Object Reference

Insecure Direct Object Reference (IDOR) is a type of vulnerability that occurs when an application leaks internal implementation information about resources to the outside world. This is most commonly seen when auto incremental IDs of the database are used as the object reference in a URL. 

https://my-shopping-site.com/orders/id/1234

If you've bought anything on the Internet, you would have seen a URL like that when browsing your past orders. Have you ever tried changing the 1234 part of the URL and seeing what happens? If it showed you some other user's order information—congratulations, you just discovered an IDOR vulnerability with a broken access control system. The IDOR vulnerability exposes enough information so that the pattern of the reference for internal resources (in this case, orders) is clear. Coupled with a broken access control system, the request for this resource goes through without checking whether the resource should actually be accessible by the user. In a correctly functioning system, only your orders should be visible to you.

How Do We Fix That?

To address the IDOR vulnerability, you could use a UUID in your Laravel model and expose that when referencing resources from outside. For the broken access control issue, the solution would differ based on what type of access control implementation you have. Regardless, the overarching logic would remain the same and would go something like this: 

  • Intercept requests for a resource

  • Check whether the requesting user has permission to view that type of resource

  • Check whether the requesting user has permission to view that particular resource

As mentioned earlier, sometimes you need this specificity and sometimes you don't. It all depends on the use case. 

Now let's continue with the examples. 

Applying Access Control Only on the Front End

This is one of those software design choices that will go spectacularly wrong, given enough time. If you move your access control mechanism to the front-end portion of your application, always make sure that the back end still validates the information coming from the user's end.

Laravel Broken Access Control Guide: Examples and Prevention image

Access control on the front end.

Take this form, for example. Here, only customers who have upgraded to the premium plan can change the options indicated by the golden arrow. If a regular user tries to change the values and save the form, an error will be shown to them. Now, this seems a reasonable flow on the surface. But if the access control system doesn't apply these same rules on the back end of the system, your application would be vulnerable. 

The user can use modify the HTML or the payload sent by the form using tools built right into the web browser. Therefore, without back-end validation, the user can get themselves a free upgrade to your system. 

How Do We Fix That?

Access control validation should always happen in the back end. Once you have it working on the back end, feel free to apply it to the front end as well. 

Not Using a Deny-First Approach

A deny-first approach (also known as default deny) denies all access to a resource by default. You need to explicitly define the permissions and the relationships between the user role and the resource. Once this is done, you can allow access to the resource. Any request attempt for a vc resource that is not explicitly allowed is automatically denied by the access control system. 

The alternative to this is an allow-first approach where specific permissions are explicitly denied and all else is allowed. The problem with this approach is that if you add a new permission interaction and don't define the deny rules correctly, unauthorized access errors might occur. This can leave your application vulnerable if you're not careful. 

How Do We Fix That?

Use a deny-first approach. If you're using Laravel Policies, start by defining deny as the default position and then explicitly defining the allow conditions.

/**
 * Determine if the given post can be updated by the user.
 *
 * @param  \App\Models\User  $user
 * @param  \App\Models\Post  $post
 * @return \Illuminate\Auth\Access\Response
 */
public function update(User $user, Post $post)
{
	if ($post->isOwner($user)) {
		return Response::allow();
	} else if ($post->organization->id == $user->organization->id) { 
		return Response::allow()
	} else {
		// The default case 
		return Response::deny('You cannot edit this post.');
	}
}

As shown in the code above, if the system encounters a case that it doesn't know about, it will just deny access to the resource. This is much better behavior than exposing sensitive information to an unintended recipient. 

Add Automated Security Testing to Your Pipeline for Free

Toward a Secure Future

In this post, we learned about broken access control systems and the damage they can cause. We also learned about some basic mistakes that we should be avoiding and how to fix them. Due to the nature of this vulnerability, broken access control errors are very domain-specific. Therefore, it's always a good idea to invest in tools and configure them to work with your application and monitor it for vulnerabilities. As the saying goes, forewarned is forearmed. 

This post was written by John Pereira. John is a technology enthusiast who's passionate about his work and all forms of technology. With over 15 years in the technology space, his area of expertise lies in API and large scale web application development, and its related constellation of technologies and processes.


StackHawk  |  November 9, 2021