Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Seeing what is not yours (broken access control)

After this lesson: I can say whether a request should have been allowed.

55 min lesson3 commandsOWASP Juice Shop lab: a vulnerable web shop to attack in your own copy

Invoice 1041 and invoice 1042

You pay a bill online and the address of the page ends in 1041. Out of curiosity you change it to 1042.

The page shows somebody else's bill: their name, their address, what they bought. You did not break anything. The site simply never asked whose bill it was.

The one rule

Warning

This lab is a shop that is insecure on purpose. Attack your own private copy and nothing else. It has no way out to the internet. Never try these things on a site that is not yours: in the UK that is a crime under the Computer Misuse Act 1990.

By the end of this lesson

Note

I can say whether a request should have been allowed.

Key words

WordWhat it meansExample
Authenticationproving who you area password
Authorisationchecking what you are allowed to do
Rolea named set of rightsadmin
Identifiera number or name that picks out one recordorder 17
Tokena signed value a browser sends to show it has signed in
Status codea number at the start of a reply that says how it went401

The cells students fill in are left blank in this preview.

Signing in is not being allowed

A site asks two questions. The first is authentication: who are you? The second is authorisation: may you do this?

Many weak sites ask the first and forget the second. They know you are a customer, so they show you whatever you ask for, including what belongs to other customers.

A status code says how a request went

Every reply starts with a status code. 200 means here it is. 401 means the site does not know who you are. 403 means it knows, and the answer is no.

The Runs below ask your lab's shop for three things without signing in. Watch which ones it hands over.

A page for everybody: one product

Command
curl -s -o /dev/null -w "%{http_code}\n" http://juice-shop:3000/api/Products/1

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

A page for signed-in people: the user list

Command
curl -s -o /dev/null -w "%{http_code}\n" http://juice-shop:3000/api/Users

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

What the shop says when it refuses

Command
curl -s http://juice-shop:3000/rest/basket/1 | grep -o "<title>[^<]*</title>"

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Worked example: reading the three answers

RequestStatusWhat it tells you
/api/Products/1200A product is public, so anybody may read it
/api/Users401The shop wants to know who is asking before it lists people
/rest/basket/1401A basket is private. The shop wants a token first

The cells students fill in are left blank in this preview.

An id in an address can be changed

Orders, baskets and invoices have numbers. The number often sits in the address. Anyone can change it.

So the site must check, on every request, that this account owns this record or holds a role that may read it. A number that is hard to guess is not a check.

Worked example: a short request log

UserRoleRequestAllowed?
anacustomerGET /orders/17 (hers)Yes, she owns it
anacustomerGET /orders/18 (Bob's)No, it is not hers
samadminGET /orders/18Yes, an admin may read any order
bobcustomerGET /admin/usersNo, only an admin may

The cells students fill in are left blank in this preview.

A common mistake

Warning

Many people think hiding a button or a link protects a page. It does not. A browser can ask for any address, whether a link points to it or not. The check must be in the server code that answers the request. Hide the link too, but never rely on it.

The server checks every request

A safe handler does three things in order. It finds out who is asking. It loads the record. It checks the owner or the role, and refuses if neither fits.

Refuse with 403. Do the check on the server, in one place, so that no page forgets it.

An owner-or-admin check

Code: python
def may_read(user, role, owner):
    if role == "admin":
        return True
    return user == owner

# Change the names and press Run. Then let a "support" role read too.
print(may_read("ana", "customer", "ana"))
print(may_read("ana", "customer", "bob"))
print(may_read("sam", "admin", "bob"))

About this lab and its source: OWASP Juice Shop lab: a vulnerable web shop to attack in your own copy

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.