Brendan Enrick

Daily Software Development

URL Parameters - Code Audits #1

This is the first post in a series that I am writing that focuses on some interesting finds from different code bases I’ve seen over my years looking at lots of different software projects. Code Audits are one place where lots of interesting bits of code can be found, since you're combing through an entire software system. I will be explaining what I’ve seen, why it’s wrong, and usually the better approach if it’s not obvious.

URL Parameters

URLs let you control the navigation as your user navigates around your site. Users may not know what URLs mean or how they work, but most people browsing the web know that URLs exist. A good number of them know that modifying that URL will take them to different pages. Even my mother knows that much about URLs.

It’s not just a tech savvy computer programmer or hacker who can figure out that changing the URL might get them to a page they’re trying to see. This is sometimes useful on site’s whose navigation is somewhat lacking. It’s also often possible to guess the page you’re looking for by adding “/login” or some other common word to find a page whose link you’re not seeing.

The Bad

On web sites that have user accounts associated with multiple accounts, it’s quite common for the current account that the user is viewing to be specified in the address bar using a query string parameter. Normally, this means that there will be something along the lines of “?AccountId=123456” on the end of the URL. It’s also very common for those to be built in as “subfolders” like this “/Account/123456/”. Either way, it’s easily visible, modifiable information presented directly to the user.

As I am sure most of you can predict, I am going to point out that I’ve seen some code that didn’t do the proper protection to make sure that only an allowed user can access any given AccountId. The site will only display the links to the allowed accounts, but the site itself does not protect against a user changing that AccountId.

If you want to get to another user’s account, you can just type in another AccountId into the URL and the site will take you there…

The circumstances where these mostly come up is when 1 user account is allowed to have access to more than one account. It’s on these sites that the user will need to be able to change accounts, and sometimes users keep multiple accounts open at the same time, so the URL is needed to keep both open at the same time. It’s important, however, that we maintain at least some level of security here.

The Better

When we build sites, it’s extremely important that we validate inputs given by users to make sure that they’re valid. It’s also important to make sure that users have access to the information they’re requesting.

The first thing you do when you receive user input is to validate that input. Obviously if you consider a URL to be user input, you would validate that immediately as well. This means that either directly or through some layer of abstraction, your code should be verifying a user has access to any information being requested.

Citigroup

There was a big fiasco last year related to Citigroup. (http://www.nytimes.com/2011/06/14/technology/14security.html) The rumor is that they had a security breach based on this type of issue. I have no real, credible information about this, but it makes for a good example even if it’s only rumor.

From the New York Times article we get these:

Once inside, they leapfrogged between the accounts of different Citi customers by inserting vari-ous account numbers into a string of text located in the browser’s address bar.

That sure sounds to me like what I just described. If it is what I described, then this security expert stretched the truth quite a bit based on what is in that same article.

One security expert familiar with the investigation wondered how the hackers could have known to breach security by focusing on the vulnerability in the browser. “It would have been hard to prepare for this type of vulnerability,” he said. The security expert insisted on anonymity because the inquiry was at an early stage.

It’s not hard to prepare for that kind of vulnerability. I guess if you have it, it’s hard to track and log the issue. What’s easy is not having the issue to begin with. It’s a simple policy of making sure that all inputs are validated for permissions before being executed. Also, that is not a “vulnerability in the browser”.

More Code Audit Nuggets

Keep watching for more interesting nuggets of stuff that I’ve seen in codebases.

Comments (4) -

  • Ken

    7/4/2012 12:03:23 PM | Reply

    So can't all this be solved by using POST vs. GET? I tend to think why would you use GET ever when you are sending form data? I simple google query says "The 'GET' method should be used when the form processing is 'idempotent', and in those cases only. As a simplification, we might say that 'GET' is basically for just getting (retrieving) data" but even if you are just querying a users log in credentials it seems POST is far superior i.e. the Citibank issue seemed to be GET related?

    I mean sometimes I could see it, like www.mycoolsite.com/articles/12 would be fine but anytime you do anything more than that you would want to POST your data? I am no web ninja but it just seems logical.

  • Blaise Pascal

    7/5/2012 2:15:09 PM | Reply

    POST vs. GET is a separate issue.  Imagine the two HTTP queries "GET https://bank.example.com/account/123456789/"; and "GET https://bank.example.com/account/987654321/";.  Both are GETs, and both include the account number in the URL.  Nothing inherent in that scheme prevents me from snooping on another account by simply putting a different account number in the URL.

    As Brendan says, you need to validate user inputs, and you need to remember that the URL is a user input.  For that matter, so is POST data and cookies.

    In this case, possible solutions include using a session-specific nonce calculated at sign-in and linked to the account on the server-side for a time-limited period.  It is much harder for an attacker to find another valid nonce than to simply find another account number.

  • Brendan Enrick

    7/6/2012 3:19:12 PM | Reply

    Thanks for the great question, Ken! Thanks for answering the question for me, Blaise.

    If you're posting the information, that's still a user input. It's slightly harder to mess with, but not difficult for anyone with development knowledge.

    To adjust to your article example, assume that I am only allowed to see odd numbered articles. If your code only shows me links to the articles I am allowed to see, but doesn't validate when I try to request the page, I will be able to type in article 12. It will bring up the page whether I post or get. As long as the developer forgot to check the input.

    It's always important to validate all user inputs. This is just one that doesn't seem like a user input (but it's an important one).

  • nick

    8/6/2012 2:33:33 AM | Reply

    never trust user input

Loading