What is Broken Object Level Authorisation?

Broken Object Level Authorisation all starts with an object. Objects should be looked at in the context of "Object-Oriented Programming", what I mean by that is objects are the things you think about first in designing a program and they are also the units of code that are eventually derived from the process. This can be anything, ranging from an account to an invoice to a credit note and everything in between. Usually, these objects are marked with an identifier because we need to address these objects directly and this is where issues can arise because we need to always check if the user is allowed to access that object. This might seem simple at first but I can assure you it's not, more about that later in the "testing" section of this article.

A broken Object issue occurs when the server does not properly check if the currently logged-in user or if a logged-out user can read, update or delete an object to which they do not have the rights.

Two main types of Broken Object Level Authorization

There are two types of broken authorization on objects, these can occur because either a userID is passed on to the server or an object, we will look into both.

Based on user ID

Sometimes a userID is passed on to the server, this can happen when we request all the resources for a certain user for example:

<https://google.com/get_users_search_history?userID=1234>

If we replace the userID with someone else's userID, we should not be able to get the search history of other users but when a Broken Object Level Authorization issue arises, we can view the search history of other users.

This issue is simple to solve as a developer. We simply need to check if the currently logged-in user is allowed to access those objects. In this example, we need to check if the userID from the GET parameter is the same as the userID of the object's owner.

Psuedo code:

if($_GET['userID'] === object.ownerID){
	ShowData()
}else{
	echo "You are not allowed to view this data"
	wait(5s)
	Redirect(Homepage)
}

This is the safest way to handle the exception, DO NOT USE THE FOLLOWING CODE AS IT IS NOT SAFE:

if(!$_GET['userID'] === object.ownerID){
	echo "You are not allowed to view this data"
	wait(5s)
	Redirect(Homepage)
}
	ShowData()

The data does not get rendered because of the redirect but a simple push of the back button can still enable the data to be displayed

Based on object ID

This vulnerability type can also exist because objectID's are passed to the server when the server does not properly check if the user is authorized for that object. This can happen very easily for example when a developer needs to secure some resources and some not. They might forget to secure one of the objects which should be secured.

Let’s build it

Now let’s create a file on our system and call it “API-1.py” and add the following code

import flask
from flask import request, jsonify

app = flask.Flask(__name__)
app.config["DEBUG"] = False

# Create some test data for our catalog in the form of a list of dictionaries.
books = [
    {'id': 0,
     'title': 'A Fire Upon the Deep',
     'author': 'Vernor Vinge',
     'first_sentence': 'The coldsleep itself was dreamless.',
     'year_published': '1992'},
    {'id': 1,
     'title': 'XXXXXXXXXXX - 403 FORBIDDEN',
     'author': 'XXXXXXXXXXXX - 403 FORBIDDEN',
     'first_sentence': 'XXXXXXXXXXXXX - 403 FORBIDDEN',
     'published': 'XXXXXXX- 403 FORBIDDEN'},
    {'id': 2,
    'title': 'XXXXXXXXXXX - 403 FORBIDDEN',
     'author': 'XXXXXXXXXXXX - 403 FORBIDDEN',
     'first_sentence': 'XXXXXXXXXXXXX - 403 FORBIDDEN',
     'published': 'XXXXXXX- 403 FORBIDDEN'}
]

books2 = [
    {'id': 0,
     'title': 'A Fire Upon the Deep',
     'author': 'Vernor Vinge',
     'first_sentence': 'The coldsleep itself was dreamless.',
     'year_published': '1992'},
    {'id': 1,
     'title': 'The Ones Who Walk Away From Omelas',
     'author': 'Ursula K. Le Guin',
     'first_sentence': 'With a clamor of bells that set the swallows soaring, the Festival of Summer came to the city Omelas, bright-towered by the sea.',
     'published': '1973'},
    {'id': 2,
     'title': 'Dhalgren',
     'author': 'Samuel R. Delany',
     'first_sentence': 'to wound the autumnal city.',
     'published': '1975'}
]

@app.route('/', methods=['GET'])
def home():
    return '''<h1>Distant Reading Archive</h1>
<p>endpoints:
<ul>
<li>/api/v1/resources/books/all</li>
<li>/api/v1/resources/books?id=xxxx</li>
</ul>
</p>'''

@app.route('/api/v1/resources/books/all', methods=['GET','POST'])
def api_all():
    return jsonify(books)

@app.route('/api/v1/resources/books', methods=['GET','POST'])
def api_id():
    # Check if an ID was provided as part of the URL.
    # If ID is provided, assign it to a variable.
    # If no ID is provided, display an error in the browser.
    if 'id' in request.args:
        id = int(request.args['id'])
    else:
        return "Error: No id field provided. Please specify an id."

    # Create an empty list for our results
    results = []

    # Loop through the data and match results that fit the requested ID.
    # IDs are unique, but other fields might return many results
    if flask.request.method == 'POST':
        for book in books2:
            if book['id'] == id:
                results.append(book)
    else:
        for book in books:
            if book['id'] == id:
                results.append(book)

    # Use the jsonify function from Flask to convert our list of
    # Python dictionaries to the JSON format.
    return jsonify(results)

app.run(host="0.0.0.0",port="5009")

Finally, run the code with “python3 API-1.py

A flask server should now be starting up on port 5009.

Untitled

You can navigate to your API by executing a curl request.