Do NOT use guids for secure object identifiers
A common problem for web application developers is how to refer to things securely. If I want a user to access, for instance, a holiday booking, I usually need to pass an argument in the url, something like booking=123456.
The basic problem with direct object reference is that the user can easily change the number to something else and then what happens? Hotel Hippo should have known that this is security 101 and the usual answer is to make sure the user is logged in and then check that the user has access to the booking they are trying to access. This basic check makes sure that the permissions are correct but there are places where the user is not and cannot be logged in to perform this check.
Imagine a password reset email. You send a link and it contains something like http://mysite.com/passwordreset/userid=123456, which will be clicked on to reset this user. The same problem occurs here. What happens if I change the number? I can access the reset page for another user which might expose private data like a security question to me. In this case, the user is resetting their password and cannot login yet so you can't actually check they have permission to do this.
What do we do? We use a nonce - a Number used ONCE - to add to the URL the combination of this and the userid means that we cannot just change the userid to another id - it won't match the nonce that we stored in the database and therefore won't validate. All good so far but where do we get the number from?
We need a 'number' or code that is statistically hard to guess, although it does not have to be cryptographically secure. The number is valid for a relatively short period after which it is deleted. There are never more than a handful of these at any point in time so being able to guess a userid/nonce combination is as good as impossible.
When I first did this, I thought, "guid". globally unique, relatively long and easy to generate in SQL or code. I would remove the dashes so it is just a hex number and we're all good right? Nope. The problem with a guid is that although it looks random, it is not actually random. It is a combination of data, time and the mac address of the system it is generated on, which is why it is globally unique but also why it is definitely not secure or statistically hard to guess. Generate one on a system and you have a very small subset of values that you know will be used in the future. In other words, your attack entropy is very small, MUCH smaller than a genuinely randomly created nonce.
There are various ways to create secure numbers for this purpose (rand or mt_rand in php, SecureRandom in .Net etc.) and these should be used, NOT guids. In some cases, there are not even two parameters, just one object reference, one guid - do not assume they cannot be guessed. You have been warned.
The basic problem with direct object reference is that the user can easily change the number to something else and then what happens? Hotel Hippo should have known that this is security 101 and the usual answer is to make sure the user is logged in and then check that the user has access to the booking they are trying to access. This basic check makes sure that the permissions are correct but there are places where the user is not and cannot be logged in to perform this check.
Imagine a password reset email. You send a link and it contains something like http://mysite.com/passwordreset/userid=123456, which will be clicked on to reset this user. The same problem occurs here. What happens if I change the number? I can access the reset page for another user which might expose private data like a security question to me. In this case, the user is resetting their password and cannot login yet so you can't actually check they have permission to do this.
What do we do? We use a nonce - a Number used ONCE - to add to the URL the combination of this and the userid means that we cannot just change the userid to another id - it won't match the nonce that we stored in the database and therefore won't validate. All good so far but where do we get the number from?
We need a 'number' or code that is statistically hard to guess, although it does not have to be cryptographically secure. The number is valid for a relatively short period after which it is deleted. There are never more than a handful of these at any point in time so being able to guess a userid/nonce combination is as good as impossible.
When I first did this, I thought, "guid". globally unique, relatively long and easy to generate in SQL or code. I would remove the dashes so it is just a hex number and we're all good right? Nope. The problem with a guid is that although it looks random, it is not actually random. It is a combination of data, time and the mac address of the system it is generated on, which is why it is globally unique but also why it is definitely not secure or statistically hard to guess. Generate one on a system and you have a very small subset of values that you know will be used in the future. In other words, your attack entropy is very small, MUCH smaller than a genuinely randomly created nonce.
There are various ways to create secure numbers for this purpose (rand or mt_rand in php, SecureRandom in .Net etc.) and these should be used, NOT guids. In some cases, there are not even two parameters, just one object reference, one guid - do not assume they cannot be guessed. You have been warned.