This article is part of a series on the OWASP Top 10 for ASP.net Core. See below for links to other articles in the series.

Broken Access Control is a new entry into the OWASP Top 10. In previous years there were concepts called “Insecure Direct Object References” and “Missing Function Level Access Controls” which have sort of been bundled all together with a couple more additions. This entry reminds me quite a bit of Broken Authentication because it’s very broad and isn’t that specific. It’s more like a mindset with examples given on things you should look out for rather than a very particular attack vector like SQL Injection. None the less, we’ll go through a couple of examples given to us by OWASP and see how they work inside .NET Core.

What Is Broken Access Control

Broken Access Control refers to the ability for an end user, whether through tampering of a URL, cookie, token, or contents of a page, to essentially access data that they shouldn’t have access to. This may be a user being able to access somebody elses data, or worse, the ability for a regular user to elevate their permissions to an admin or super user.

The most common vulnerability that I’ve seen is code that verifies that a user is logged in, but not that they are allowed to access a particular piece of data. For example, given a URL like www.mysite.com/orders/orderid=900 what would happen if we changed that orderid by 1? What if a user tried to access www.mysite.com/orders/orderid=901. There will likely be code to check that a user is logged in (Probably a simple Authorize attribute in .NET Core). But how about specific code to make sure that the order actually belongs to that particular customer? It’s issues like this that make up the bulk of the Broken Access Control.

Where I start to veer away from the official definition from OWASP is that the scope starts exploding after this. I think when you try and jam too much under a single security headline, it starts losing it’s meaning. SQL Injection for example is very focused and has very specific attack vectors. Access Control is much more broader subject. For example, the following is defined as “Broken Access Control” by OWASP :

Misconfigured or too broad CORS configuration

Web server directory listing/browsing

Backups/Source control (.git/.svn) files present in web roots

Rate limiting of APIs

JWT Tokens not being invalidated on logout

And the list goes on. Essentially if you ask yourself “Should a web user be able to access this data in this way”, and the answer is “no”, then that’s Broken Access Control in a nutshell.

Insecure Direct Object References

As we’ve already seen, this was probably the grandfather of Broken Access Control in the OWASP Top 10. Direct object references are id’s or reference variables that are able to be changed by an end user, and they can then retrieve records that they should not be privy to.

As an example, let’s say we have the following action in a controller :

[HttpGet("order/{id}")] [Authorize] public IActionResult GetOrder(int id) { Order order = _orderRepository.GetById(id); return Ok(order); }

Now this is authorized somewhat as seen by the authorize header. This means a completely anonymous user can’t hit this endpoint, but it doesn’t stop one logged in user accessing a different user’s orders. Any id passed into this endpoint will return an order object no questions asked.

Let’s modify it a bit.

[HttpGet("order/{id}")] [Authorize] public IActionResult GetOrder(int id) { var loggedInUser = HttpContext.User; var customerId = loggedInUser.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value; Order order = _orderRepository.GetById(id); if(order.CustomerId != customerId) { return Unauthorized(); } return Ok(order); }

This will require a bit of imagination, but hopefully not too much!

Imagine that when we log in our user, we also store their customerid in a claims. That means we forever have access to exactly who is logged in when they try and make requests, not just that they are loggedin in general. Next, on each Order we store the CustomerId of exactly who the order belongs to. Finally, it means that when someone tries to load an order, we get the Id of the customer logged in, and compare that to the CustomerId of the actual order. If they don’t match, we reject the request. Perfect!

An important thing to note is that URL’s aren’t the only place this can happen. I’ve seen hidden fields, cookies, and javascript variables all be susceptible to this kind of abuse. The rule should always be that if it’s in the browser, a user can modify it. Server side validation and authorization is king.

One final thing to mention when it comes to direct object references is that “Security through obscurity” is not a solution. Security through obscurity refers to a system being secure because of some “secret design” or hidden implementation being somewhat “unguessable” by an end user. In this example, if we change our initial get order method to :

[HttpGet("order/{id}")] [Authorize] public IActionResult GetOrder(Guid id) { Order order = _orderRepository.GetById(id); return Ok(order); }

So now our OrderId is a Guid. I’ve met many developers that think this is now secure. How can someone guess a Guid? It is not feasible to enumerate all guids against a web endpoint (If it was feasible than we would have many more clashes), and this much is true. But what if someone did get a hold of that guid from a customer? I’ve seen things like a customer sending a screenshot of their browser to someone else, and their Order Guid being in plain sight. You can’t predict how this Guid might leak in the future, malicious or stupidness. That’s why again, server side authorization is always king.

CORS Misconfiguration

CORS refers to limiting what website can place javascript on their page to then call your API. We have a great tutorial on how to use CORS in ASP.net Core here. In terms of OWASP, the issue with CORS is that it’s all too easy to just open up your website to all requests and call it a day. For example, your configuration may look like the following :

public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod()); app.UseMvc(); }

Here you are just saying let anything through, I don’t really care. The security problem becomes extremely apparent if your API uses cookies as an authentication mechanism. A malicious website can make a users browser make Ajax calls to the API, the cookies will be sent along with the request, and sensitive data will be leaked. It’s that easy.

In simple terms, unless your API is meant to be accessed by the wider public (e.g. the data it is exposing is completely public), you should never just allow all origins.

In ASP.net Core, this means specifying which websites are allowed to make Ajax calls.

public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseCors(options => options.WithOrigins("http://www.mydomain.com").AllowAnyMethod()); app.UseMvc(); }

As with all security configurations, the minimum amount of access available is always the best setting.

Directory Traversal and Dangerous Files

I lump these together because they should be no-brainers, but should always be on your checklist when deploying a site for the first time. The official line in the OWASP handbook when it comes to these is :

Disable web server directory listing and ensure file metadata (e.g. .git) and backup files are not present within web roots

Seems pretty simple right? And that’s because it is.

The first is that I’ve actually never had a reason to allow directory traversal on a website. That is, if I have a URL like www.mysite.com/images/image1.jpg , a user can’t simply go to www.mysite.com/images/ and view all images in that directory. It may seem harmless at first, like who cares if a user can see all the images on my website. But then again, why give a user the ability to enumerate through all files on your system? It makes no sense when you think about it.

The second part is that metadata type files, or development only files should not be available on a web server. Some are obviously downright dangerous (Site backups could leak passwords for databases or third party integrations), and other development files could be harmless. Again it makes zero sense to have these on a web server at all as a user will never have a need to access them, so don’t do it!

Summary

While I’ve given some particular examples in this post, Broken Access Control really boils down to one thing, If a user can access something they shouldn’t, then that’s broken access control. While OWASP hasn’t put out too much info on this subject yet, a good place to start is the official OWASP document for 2017 (Available here). Within .NET Core, there isn’t any “inherit” protection against this sort of thing, and it really comes down to always enforcing access control at a server level.

Next post we’ll be looking at a completely new entry to the OWASP Top 10 for 2017, XML External Entities (XXE).