Insecure Direct Object Reference - Security Series #15

The first time I looked at the OWASP Top Ten web vulnerabilities, they all made sense to me, save for one. That one was A4 - Insecure Direct Object Reference. At the time I was still pretty new to object-oriented programming and so the first thing I thought was that it was referring to those kinds of objects.

But that is not what they are talking about. The are talking about any direct reference to an "implementation object". Meaning objects like files, folders, database records, or other types of "keys".

Direct Object Reference

First we need to learn what a direct object reference is. As I said previously, an object, in this case, is anything that we can make a reference to from our application that can be implemented directly. The primary concern is when we reference them from an untrusted input (FORM, URL, CGI, and COOKIE).

For example,


http://www.12robots.com/getfile.cfm?filename=jasonsLittleBlackbook.txt

In this URL it is pretty obvious that I am directly referencing a specific file. This immediately flags this URL as one that could be open to a number of attacks. When a hacker sees this, they could try a path traversal attack against it (i.e. 'filename=../../../../../../../tomcat/conf/tomcat-users.xml') or they could try an enumeration attack where they simple try to pass in different file names to see what they get (i.e. 'filename=jasonsPassword.txt').

Both of these attacks can be automated. I hope it becomes obvious that this is a vulnerability and that we would need to build a lot of security around this to ensure it was not abused. However, if we can remove the direct object reference, we can make our lives easier.

Here are some other examples of direct object references we might want to remove. These all use URL parameters, but keep in mind that ANY untrusted scope is vulnerable to this.


http://www.12robots.com/myaccount.cfm?userid=1


http://www.12robots.com/search.cfm?keyword=jason&table=news


<!--- This one may have come from a <select> box where the user is prompted on which credit card from their account they have on file to use for the checkout --->
http://www.12robots.com/checkoutProcessing.cfm?whichCreditCard=8754

A more detailed example

Let's take one of the examples from above and dig a little deeper. I like the credit card example, we'll work with that.

In our vulnerable e-commerce application we have a checkout process that asks the user which credit card, from those on file, they would like to use for this transaction. The code for showing the credit card choices looks like this.


<select name="whichCard">
    <cfloop query="qGetCardsOnFile">
        <option value="#qGetCardsOnFile.cardID#">#qGetCardsOnFile.cardno#</option>
    </cfloop>
</select>

Which results in this select box


<select name="whichCard">
    <option value="4567">1111222233334444</option>
    <option value="4890">2222333344445555</option>
    <option value="8766">3333444455556666</option>
</select>

Now this select box may be less than ideal for a number of reasons, but the one we are looking at is that it is using a direct object reference where it is referencing which card will be used.

A hacker could use this information and deduce that the credit card id numbers are assigned by predictable increments and then after submitting their own checkout form(but before the form gets to the server) they could intercept it and change the value of the whichCard parameter to, for example, "8767". 8767 likely corresponds, in the database, to a current card belonging to another user. If the card is valid and there are no other checks in place to stop an end user from doing this, then the transaction could go through but be charged to another user's credit card.

We need to remove this direct object reference.

Removing the direct object reference

In the example above we can easily remove the direct object reference and ensure that they user cannot manipulate the parameter, and the system, as we saw previously.


<cfset userCards = ArrayNew(2) />

<cfloop query="qGetCardsOnFile">
    <cfset userCards[currentrow][1] = qGetCardsOnFile.cardID/>
    <cfset userCards[currentrow][2] = qGetCardsOnFile.cardno />
</cfloop>

<cfset session.userCards = userCards />

<cfoutput>
    
    <select name="whichCard">
        <cfloop from="1" to="#ArrayLen(userCards)#" index="cardIndex">
            <option value="#userCards[cardIndex][1]#">#userCards[cardIndex][2]#</option>
        </cfloop>
    </select>
</cfoutput>

This will result in a select box that looks like this


<select name="whichCard">     
    <option value="1">1111222233334444</option>
    <option value="2">2222333344445555</option>
    <option value="3">3333444455556666</option>
</select>

In this code, we are taking the values from the query that contains the card numbers and sticking them into a multidimensional array (you could also use a struct or an object). We then store that array in the user's session and use the array, instead of the query, to build the select box. Each value of the select options corresponds to a position in the array instead of to a specific database record.

Now, instead of being given an option to choose a card based on a direct reference to the database, we have a direct reference to an array in the user's session, and that array contains only that user's data. Now he can manipulate parameters all he wants and will never get a credit card other than his own.

Now in whatever page or function that processes this form, the developer can use the passed value and the userCards array that is stored in the session to figure out which card they want to use.

Conclusion

This example is not the most elegant way of implementing this solution, but it is a straight forward way. The OWASP ESAPI project has a Java object called the AccessReferenceMap that deals with this pretty well. I have begun creating an AccessReferenceMap object for ColdFusion, but it is not ready yet. Someday I will need to finish that.

Comments
Rick O's Gravatar You should be careful that you don't open yourself up to an XSS vulnerability when you do this. By making the param a monotonic number, you're just asking for some nasty javascript to iterate through the available numbers and squeeze out every last drop of personal information.

I'd say it was more important to have paranoia checking in your SQL. In this specific example, your WHERE clause wouldn't just include (cardID = ?) but also AND (ownerId = ?). The side effect of this could even be faster data access -- a composite index on those two columns would lead to better cardinality and a faster query.
# Posted By Rick O | 1/19/10 1:51 PM
Jason Dean's Gravatar @rick,

I don't see how this could open anything up to an XSS attack unless the number, after being passed in an untrusted scope, was on a page. And even if it was, unless proper XSS countermeasures are used, it does not matter if it is a number or not. But in this case, the numbers that I am using are not coming from an untrusted source and I am not indicating how they are being used, but there is no reason to think that they are begin rendered to the page in anyway.

As for your second point, I agree, that would be the right way to handle the credit card information, and I was not trying to suggest that using indirect object reference was the only way to protect against this type of attack, I was using a contrived example of how this attack might be performed. I hope you are not reading into my post more than what is there.

In keeping with the principle of "defense in depth" I would say that there is no reason not to implement both countermeasures. But as I said I am not discussing this particular situation in such depth, I was merely making an example.
# Posted By Jason Dean | 1/19/10 2:27 PM
Ben Nadel's Gravatar It's scary to think about the kind of information we give out in our code without thinking about it too deeply. I use ID's ALLL the time when submitting form data.

I agree with Rick, however, that you should always be double-checking your JOIN integrity at the SQL level as well.... though I don't see XSS as a vulnerability.

Great post.
# Posted By Ben Nadel | 1/23/10 8:43 PM
Jason Dean's Gravatar @Ben,

Thanks for the comment.

I hope it was clear that I was not disagreeing with Rick on the JOIN issue, I was just trying to make the point that maybe in this case that would help, it was only an example, and that we need to be aware of this vulnerability so that in other cases, we can protect against it. Perhaps it was a bad example, but I don't want the point of the post derailed by something unrelated.

I will bring up the principle of defense-in-depth, which says, that even if we can protect our application in one way, it should not prevent us from adding additional protection where we can, if we reasonably can.
# Posted By Jason Dean | 1/23/10 9:59 PM
Ben Nadel's Gravatar @Jason,

No no, I knew you weren't disagreeing with Rick. I was just adding my 2 cents.
# Posted By Ben Nadel | 1/24/10 5:02 PM
Santosh Parida's Gravatar Hi,

I am santosh here. I want to know, how to restrict URL
parametres. If we will use session values against those then
is it right thing ? In my application I am using mostly URL
parametres. Pls. suggest. Thanks in advance.
# Posted By Santosh Parida | 5/20/10 12:51 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner