API design and security: Why hide internal ids?

API design and security: Why hide internal ids?

I’ve heard a few people say that you should never expose your internal ids to the outside world (for instance an auto_increment’ng primary key).
Some suggest having some sort of uuid column that you use instead for lookups.
I’m wondering really why this would be suggested and if it’s truly important.
Using a uuid instead is basically just obfuscating the id. What’s the point? The only thing I can think of is that auto_incrementing integers obviously point out the ordering of my db objects. Does it matter if an outside user knows that one thing was created before/after another?
Or is it purely that obfuscating the ids would prevent “guessing” at different operations on specific objects?
Is this even an issue I should thinking about when designing an external facing API?


Solution 1:

Great answers, I’ll add another reason to why you don’t want to expose your internal auto incremented ID.
As a competitive company I can easily instrument how many new users/orders/etc you get every week/day/hour. I just need to create a user and/or order and subtract the new ID from what I got last time.
So not only for security reasons, it’s business reasons as well.

Solution 2:

Any information that you provide a malicious user about your application and its layout can and will be used against your application. One of the problems we face in (web) application security is that seemingly innocuous design decisions taken at the infancy of a project become achilles heels when the project scales larger. Letting an attacker make informed guesses about the ordering of entities can come back to haunt you in the following, somewhat unrelated ways:

  1. The ID of the entity will inevitably be passed as a parameter at some point in your application. This will result in hackers eventually being able to feed your application arguments they ordinarily should not have access to. I’ve personally been able to view order details (on a very popular retailer’s site) that I had no business viewing, as a URL argument no less. I simply fed the app sequential numbers from my own legitimate order.

  2. Knowing the limits or at least the progression of primary key field values is invaluable fodder for SQL injection attacks, scope of which I can’t cover here.

  3. Key values are used not only in RDBMS systems, but other Key-Value mapping systems. Imagine if the JSESSION_ID cookie order could be predetermined or guessed? Everybody with opposable thumbs will be replaying sessions in web apps.

And many more that I’m sure other ppl here will come up with.

SEAL team 6 doesn’t necessarily mean there are 6 seal teams. Just keeps the enemy guessing. And the time spent guessing by a potential attacker is more money in your pocket any way you slice it.

Solution 3:

As with many security-related issues, it’s a subtle answer – kolossus gives a good overview.

It helps to understand how an attacker might go about compromising your API, and how many security breaches occur.

Most security breaches are caused by bugs or oversights, and attackers look for those. An attacker who is trying to compromise your API will firstly try to collect information about it – as it’s an API, presumably you publish detailed usage documentation. An attacker will use this document, and try lots of different ways to make your site crash (and thereby expose more information, if he’s lucky), or react in ways you didn’t anticipate.

You have to assume the attacker has lots of time, and will script their attack to try every single avenue – like a burglar with infinite time, who goes around your house trying every door and window, with a lock pick that learns from every attempt.

So, if your API exposes a method like getUserInfo(userid), and userID is an integer, the attacker will write a script to iterate from 0 upwards to find out how many users you have. They’ll try negative numbers, and max(INT) + 1. Your application could leak information in all those cases, and – if the developer forgot to handle certain errors – may expose more data than you intended.

If your API includes logic to restrict access to certain data – e.g. you’re allowed to execute getUserInfo for users in your friend list – the attacker may get lucky with some numbers because of a bug or an oversight, and he’ll know that the info he is getting relates to a valid user, so they can build up a model of the way your application is designed. It’s the equivalent of a burglar knowing that all your locks come from a single manufacturer, so they only need to bring that lock pick.

By itself, this may be of no advantage to the attacker – but it makes their life a tiny bit easier.

Given the effort of using UUIDs or another meaningless identifier, it’s probably worth making things harder for the attacker. It’s not the most important consideration, of course – it probably doesn’t make the top 5 things you should do to protect your API from attackers – but it helps.