This article will describe how to protect your J2EE application from direct object reference (DOR) attacks using ESAPI. As with all of the detail articles in this series, if you need a refresher on OWASP or ESAPI, please see the intro article The OWASP Top Ten and ESAPI.
What’s the problem?
What is direct object reference (DOR)? Other than being a somewhat quirky, generic name … here’s what OWASP says it is:
“A direct object reference occurs when a developer exposes a reference to an internal implementation object, such as a file, directory, database record, or key, as a URL or form parameter. An attacker can manipulate direct object references to access other objects without authorization, unless an access control check is in place. ” [from here]
So, what does that mean? Well for most (not all) J2EE developers, it will mean database key references, so that’s what we’ll deal with here. However, the discussion is applicable no matter what your reference.
Consider the following database table called USERS.
Now, let’s say that this database table were queried in a normal application. Then, the results are presented on the page, and each record in the result list would probably have a link to more detail about each user, having their pictures, their list of friends, etc. This is the typical master-detail interaction and is prevalent in pretty much any web app, not just J2EE. But how do we get to the detail page? How do we pass along which user we want to see in detail from the “master” table to “detail” page? Well, we could start by using the first name, but that’s not unique, or we could use the last name, but same issue. We could include both, which is less likely to produce duplicates but it’s still possible. In most situations, what happens is each table in the database has a column setup as a unique identifier. Each record in the table will have a different ID. So, that’s what we use. Now, here’s what the url might look like to go view greater detail about Bill:
Ok, great, so what we have is the identifier being passed in the url to represent Bill. That way when we get to the view detail code, we simply check the id that was passed in and pull up that record in detail and display the information.
So where’s the issue? Well, everything works fine in this instance b/c every user is able to see the detail, but let’s look at what this might look like for a bank, which is the classic DOR example. In a bank, all records for all users are stored in the same table (at least in our example), however each user can only view his or her account. They should not be able to simply pick a random account to view or manipulate. But what if the URL to my account looks like:
What would happen if I changed this to
If there’s no protection in place, I can see someone else’s account!
Well, this is the crux of the DOR problem. Any attacker can manipulate url or form parameters at will when they submit a request, which means this issue obviously applies to both GET and POST requests, as well as exposed parameters, hidden fields, etc. If it’s being sent from the client to the server, the client can alter it. That means when you use the ID as the reference, and that ID can be manipulated, the attacker can gain access to data that they should not have access to. The ID in this case is the direct reference (id) to the object (record in the table) that you are trying to access.
Where do we go from here? ….
Well, ok, that’s not super encouraging. We have an issue (that most people have never heard of) that is affecting most applications . Banking is the obvious example, but this could be a problem for many different types of applications. Take heart, there is a solution (actually there are two)! You have 2 options for how to properly deal with this issue when it exists in your application.
1. Use ESAPI’s AccessReferenceMap (or something similar)
2. Perform authorization on the view detail page
Bonus. Use both! (the only way to go for paranoid people like me)
Let’s discuss the basic concepts of each of the options.
The AccessReferenceMap is an interface for a map which has methods that allow you to add a “direct” reference (which generates an “indirect” reference), then retrieve an indirect reference using a direct reference, or retrieve a direct reference using an indirect reference.
Confused yet? Me too. Actually it’s quite simple. Let’s use a contrived (and insecure) example just to illustrate the concept (again, this simplified version is NOT secure).
Let’s say you have a table in the database with 4 records, and their id’s are 1,2,3,4. Now let’s say you have the situation above where you display all these records in a master table, and each row is a link to more detail about the individual record. Let’s say you want to hide these numbers and reference them on the UI as letters instead. The simple mapping would be 1=A, 2=B, etc… In this case, when you retrieve the data from the database, your ids are 1,2,3,4. However, when you show them on the screen, their ids are A,B,C,D. Finally from the detail page (after the user clicks the link), you would just lookup what C matches, find that it is 3, then retrieve that record from the DB and display it. It’s that simple.
Now, in practice, obviously a letter -> number matrix is too simple to be effective. What works better, as usual in computer science, is randomness. That’s why ESAPI already contains a class called RandomAccessReferenceMap. Here’s an example of how you might use it.
MyObject obj; // generate your object Collection coll; // holds objects for display in UI //create ESAPI random access reference map AccessReferenceMap map = new RandomAccessReferenceMap(); //get indirect reference using direct reference as seed input String indirectReference = map.addDirectReference(obj.getId()); //set indirect reference for each object - requires your app object to have this method obj.setIndirectReference(indirectReference); //add object to display collection coll.add(obj); //store collection in request/session and forward to UI ...
As you can see the code is fairly simple. Beyond this, the only other code is when it is displayed on screen, you use the indirect reference to be the id that gets you to the view detail page. From the view detail page, you retrieve the id that was passed in, then iterate over the collection in memory until you find the object from the map matching the indirect reference and then retrieve the full data from the DB by mapping back to the original ID.
A couple notes here:
– If you get an id passed in that doesn’t match any in your collection, it means either the code is wrong, or the user has tampered with it, so it should be a security issue
– You’ll note my object has a method called setIndirectReference. All my value objects in my applications extend a base class with some helper methods, and this is one of them. This is obviously not required and there are many ways to accomplish the usage of the RandomAccessReferenceMap, but I build my apps knowing I’m going to use this technique, so it’s not a problem for me.
Per page authorization is another technique that has a similar goal to the AccessReferenceMap. The idea here is that you’ve already done the appropriate authorization on the master page. Again, for simplicity, let’s use the bank account example. Let’s say you have a checking and savings account with a particular bank, and both of those accounts show up on your “master” accounts page. The code has to check what you have access to (authorization) in order to only display the appropriate 2 accounts to you on that page.
Unfortunately, that’s where many applications stop. They assume since they’ve only given you 2 accounts to choose from, you’ll have to choose 1 of the 2. This doesn’t take tampering into account, however, and tampering can allow the attacker to input any account they choose.
What the per-page authorization says is that if and when the attacker tampers with the data sent to the detail page, it still shouldn’t be possible for him to see data that is not his. The code that brings up detail about a specific account should first include a check that you have access to that account (authorization). Again, if he doesn’t have access to that account, either the code is broken, or he is tampering and that is a security incident.
So, which option should I choose
Well, either or both. They are both viable options to solve the DOR problem, but I’ll try to give a couple of points about both that may sway you one direction or the other.
1. *May* not require an extra DB hit to do detail page authorization – depends on how you’ve coded it.
2. Shields actual IDs from end user. In case you ever have a vulnerability on another page, and haven’t protected for it properly, the attacker won’t know your IDs.
3. Requires a bit of extra coding and some extra information to pass around the application in order to maintain access to the reference maps.
4. Currently not very popular, so not very well understood yet.
1. *May* require an extra DB hit to do detail page authorization – depends on how you’ve coded it.
2. Exposes actual IDs from end user. In case you ever have a vulnerability on another page, and haven’t protected for it properly, the attacker could know your IDs.
3. Doesn’t requires extra coding or the passing of information around the application in order to maintain access to the reference maps.
4. Very well understood paradigm.
Well, there you have it. There’s really not that much to it. While direct object access is a significant issue in many of today’s applications, it can be fairly simply corrected. I’ve presented a couple of options that can be done separately or combined together as a solution. I like to use them both, but separately, they still do their job properly.
Just one final safety note. While I’ve only discussed these techniques in the context of database queries, whichever one or both that you choose should additionally be applied when referencing any sensitive internal resources, including files, internal objects, classes, etc.
Well, that’s it. Hope you’ve found this article helpful. Let me know if you have any questions or suggestions to improve any of my ideas.
Other articles in this series:
Part 0: The OWASP Top Ten and ESAPI
Part 1: The OWASP Top Ten and ESAPI – Part 1 – Cross Site Scripting (XSS)
Part 2: The OWASP Top Ten and ESAPI – Part 2 – Injection Flaws
Part 3: The OWASP Top Ten and ESAPI – Part 3 – Malicious File Execution
Part 4: The OWASP Top Ten and ESAPI – Part 4 – Insecure Direct Object Reference
Part 5: The OWASP Top Ten and ESAPI – Part 5 – Cross Site Request Forgery (CSRF)
Part 6: The OWASP Top Ten and ESAPI – Part 6 – Information Leakage and Improper Error Handling
Part 7: The OWASP Top Ten and ESAPI – Part 7 – Broken Authentication and Session Management
Part 8: The OWASP Top Ten and ESAPI – Part 8 – Insecure Cryptographic Storage
Part 9: The OWASP Top Ten and ESAPI – Part 9 – Insecure Communications
Part 10: The OWASP Top Ten and ESAPI – Part 10 – Failure to Restrict URL Access