Avoiding direct object reference problems

Discussing some of the ways to prevent insecure direct object reference issues.

Avoiding direct object reference problems

Direct object references (DOR) are a common feature of web applications, a simple example would be using an ID to show a particular record.  Done incorrectly, DORs can provide access to records a user shouldn't see so let's look at how to avoid problems when using DORs.

Insecure direct object references have been part of the OWASP Top Ten for years (now merged into broken access control).  The OWASP Top Ten from 2013 has an article on Insecure Direct Object References.

IDs in URLs

Quite often when a DOR is exploited it'll be because the URL in the address bar shows something like https://example.org/user?id=15.  Any inquisitive person is likely to change the 15 to 14, or any other number, and this becomes a problem when information is shown to the user.

As a real world example, I was purchasing some certificates for one of our sites at work.  Looking at my previous orders I could see an ID in the address and on changing it I was shown someone else's order.  Initially I couldn't see what domain it was for but I could see how much they paid and what the product was.  Clearly this is a problem for the supplier as I might see they're charging me more than a third party.  Reviewing the page's source I was able to get the domain name and customer name which were available as comments in the source code.

Needless to say I reported it to the vendor.  Within 45 minutes the problem was resolved - a very good turn around!  Sadly no "hall of fame" mention or free t-shirt though.

Once you know the format of a page's address it's not too hard to write a script that scrapes data from the vulnerable site.

Changing POST data

The above example worked with GET requests, easiest controlled via the address bar of your browser.  Using a tool like Burp Suite it's possible to intercept and modify POST data (form submissions and the like), attacking a vulnerable system that way.  More technical skill is required to exploit this but it's still a risk.  Fortunately the same mitigations work for both vectors.


A few techniques exist to mitigate this vulnerability although it's worth considering implementing more than one to provide a defence in depth.

Don't send the ID

The safest option, although not always practical, is to not send the ID at all - that way the end-user, penetration tester or attacker can't exploit it at all.

In eVitabu each user has a profile telling APF about them, where the user is based, what languages they speak etc.  Clearly it would be a problem (and a data leak) if users could see someone else's data.

The "my account" menu in eVitabu

Fortunately the system already knows the logged in user's ID so there's no need for the browser to explicitly say "show me my profile, I'm user ID 15", instead eVitabu just provides a link that shows the editable profile.  In the background the system retrieves the data for the logged in user and populates the form.

As the ID is never sent by the browser it's not possible for the GET (or POST) request to cause someone else's data to be displayed.

Use non-predictable keys

Tables in databases often use an auto-incrementing, numerical, value to identify individual records.  A number only occupies a small amount of disk space, so it's very space efficient when scaling is considered.  Problem is we've also seen predictable numbers give rise to the DOR vulnerability.

Changing to use a non-predictable key helps reduce this risk.  If the address for my order had been https://example.org/order?id=b58d3b4921149f3a3ae051de0252fe0f it would have been significantly harder for me to retrieve other details - or even guess my own.  It's still possible to attack a system but there's more effort required as I'd have to brute force the ID.

The Skullduggery LARP booking system uses non-predictable keys to provide customers with a receipt they can refer to again.  There's no login required to the booking system, so we couldn't just say "login and click view my orders".  Instead we take a hash of various bits of data (could be name, order date, email address but I'm not telling) to provide an ID.

To further protect customers we clear down information held in the database after a period of time.  Naturally this means receipt links break after a while but if necessary we still have a copy in our back-office systems.

Temporary keys

Temporary keys were mentioned on a course I attended years ago although I've never used them in systems I've designed.  The basic premise of a temporary key is that it's mapped on to the original ID via a translation table.  Being temporary, the key lasts only during the current session or for a limited period of time.

Managing this approach seems to be overly burdensome to me, hence I've never used it.

Permissions checks

When a login is required it's possible to assign permissions to a user - either by traditional user levels (guest, user, admin) or using role based access control (RBAC).  RBAC is the more granular of the two, allowing a system to restrict access based on what the user does.  For example, eVitabu has roles for end-users, staff and admins.  End-users can only see their profile.  Staff can change content, add contributors, edit users etc., while admins can change how the system operates too.

Permissions checks can be made in either case.  A basic check could be simply to only show restricted content if a user was logged in (like a paywall on a news site) or to only allow settings changes if the user is an admin.  Similarly we can check the user is permitted to view or change data.

By checking permissions we can potentially allow DOR because we've secured it via the permissions system.  If every admin is allowed to change all content anyway, and changing content is already restricted to users at that level, there's no need to secure the object reference too.

Don't store the data anyway

Ideally you don't want to store unnecessary data anyway as then you can't leak it by insecure direct object reference or other means.  Regularly review the data you collect (especially following the introduction of GDPR) and determine if it's really necessary.  Needs change as systems evolve so maybe you can purge data from your system.


Direct Object References are a fact of life, the important thing is to secure them appropriately.

If you're developing web applications I thoroughly recommend you look over the OWASP Top Ten and learn how to avoid some security pitfalls.

Banner image, Raseone filing cabinet, from OpenClipart.org, by .