Staggan Posted June 6, 2014 Share Posted June 6, 2014 Hello I am looking to create an expiring token for use with our password reset system. We want tokens to be valid for a set period, let's say 24hrs. Currently we md5 the username and userid, and send this as a token to the users registered email... It's OK, but means that token is valid indefinitely. I am not keen on adding more fields to the database to store the time the request was made, so wondered if anyone had a suggestion? Is there a way I can encrypt a token including a timestamp and then decrypt it to separate the elements out to check the timestamp? Thanks Quote Link to comment Share on other sites More sharing options...
paddy_fields Posted June 6, 2014 Share Posted June 6, 2014 Add a column in your token table to store a timestamp of when the token was requested Then when the the token is used just compare the current date and time, to that of the timestamp in the token table You can then use a conditional statement to see if the condition - say less than 24 hours - has been met. Otherwise throw an error Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted June 6, 2014 Share Posted June 6, 2014 (edited) Hi, a hash of the username and the user ID? How is this secret data? And what is the hashing supposed to do? You need a secret token. That means you need to read, say, 16 bytes from the random number generator of your operating system: $raw_token = openssl_random_pseudo_bytes(16); $encoded_token = base64_encode($raw_token); If the OpenSSL extension isn't available on your system, you can also use mcrypt_create_iv() or read directly from the system device (typically /dev/urandom). Encryption doesn't help, no. This is not about keeping the timestamp secret. You need to prevent people from altering it, so this is a problem of integrity and authenticity. The theoretical solution is a message authentication code (MAC). But, frankly, don't do it. Getting MACs right is very difficult, and it will make your system much more complex and fragile. For what? Only to save two database columns? The proper solution is to send the encoded token to the user, store the timestamp and a hash(!) of the raw token in the database and then check them on request. Edited June 6, 2014 by Jacques1 Quote Link to comment Share on other sites More sharing options...
Staggan Posted June 6, 2014 Author Share Posted June 6, 2014 Thanks for the replies.. I think I have a solution, but would like some comments. When a user requests to change their password we ask for the username for their account... We then get the users email address, userid and current time from the database, and generate a token as below. md5 the username and userid, and then append the timestamp. We then use MCRYPT to encrypt the whole thing..... So now we have a token with an md5 of the username and userid, with the timestamp appended and all encrypted. This token is emailed along with a link to the user. The user then clicks the link and opens a page... This page, using MCRYPT, decripts the token, and removes the MD5 part of the token.. as all MD5 are 32 characters long... What we are left with is a valid timestamp.... We can then test the timestamp against the current server time and decide if it has expired... Thoughts? Thanks Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted June 6, 2014 Share Posted June 6, 2014 As I've already said, this makes no sense. You can't just throw a bunch of cryptography at the problem and hope that it will somehow magically make the site secure. You need to use the right tools in the right way. Hashing the username and the user ID is utterly useless. What is this supposed to do? And again: Encryption does not help you at all in your case. The purpose of encryption is confidentiality. It generally does not prevent people from altering the data. So what are you trying to hide from whom? And how does this solve the problem? Quote Link to comment Share on other sites More sharing options...
paddy_fields Posted June 6, 2014 Share Posted June 6, 2014 (edited) You haven't incorporated either of our comments into your solution As Jacques says, hashing the username is pointless - you only need to hash the password As I said previously but in more detail.... 1.a - Add a column in your token table to store a timestamp of when the token was requested 1.b - At this point a new row will be inserted into the token table, along with the new token, user id, and current timestamp 1.c - An emai will be sent to the user with the token link 2.a - The link is followed, compare the token to the database and see if they match 2.b - Find the current date and time 2.c - Compare this to the timestamp in the row, and if it meets your criteria (ie 24 hours) allow then to update the password Edited June 6, 2014 by paddyfields Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted June 6, 2014 Share Posted June 6, 2014 Do not store the plaintext token in the database. It's equivalent to a password, so you may only store its hash. But apart from that, yes, this is the standard way as used by hundreds of thousands of sites around the world. And only to clarify: The token must be unpredictable. Anything other than a bunch of random bytes does not work. Quote Link to comment Share on other sites More sharing options...
Staggan Posted June 6, 2014 Author Share Posted June 6, 2014 Sorry, I am a little confused. We are not talking about passwords here, or overal site security, we have that. What we are talking about is an easily created and timestamped token to allow us to validate a request to change a password, without the need to store additional data in the database. Using the username and userid for a hash made sense as these are two known quantities that we can easily get once we have the username. Unless someone knows the userid for a specific user they will, hopefully, be unable to replicate the token and therefore be unlikely to use this to reset someone's password. Adding the timestamp then allows us to know when a request was made, and allows us to decide if it has expired. Encrypting the whole lot, using MCRYPT as Jacques suggested, then just makes the whole process of cracking the tokens to be able to reset someone's password that much harder... What am I missing? Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted June 6, 2014 Share Posted June 6, 2014 We are not talking about passwords here, or overal site security, we have that. What we are talking about is an easily created and timestamped token to allow us to validate a request to change a password, without the need to store additional data in the database. I realize that, and the bottom line is: This is nonsensical. I don't know why exactly you need to save database columns, but this results in a much higher complexity, much more work and much more problems. That's hardly a good decision. Using the username and userid for a hash made sense as these are two known quantities that we can easily get once we have the username. Unless someone knows the userid for a specific user they will, hopefully, be unable to replicate the token and therefore be unlikely to use this to reset someone's password. The user ID is not a secret. Those are just sequential numbers, right? Then it's not exactly hard to guess the ID of other users, is it? Even worse: This “token” is valid for the entire lifetime of the user account. Once an attacker has gotten hold of it, they can use it as often as they want. And hashing the user ID does not help you at all. If an attacker knows the user ID, they can simply calculate the MD5 hash from it. Nothing prevents them from doing that. And if they know the hash, well, then they already got the token. So the hash does absolutely nothing. You might as well append the letter “x” to all IDs. Long story short: This “token” does not work. Encrypting the whole lot, using MCRYPT as Jacques suggested, then just makes the whole process of cracking the tokens to be able to reset someone's password that much harder... It did not suggest encryption. You're putting words in my mouth. I specifically adviced against encryption and explained exactly why it does not work for your case. It's as useless as that MD5 hash. Sorry, but I think this discussion is pointless. You don't even seem to read the replies, let alone take the advice. We posted two valid solutions, one of them involving database columns, one of them not. If you stick to your approach no matter how broken it is, go ahead. But you don't need us then. Quote Link to comment Share on other sites More sharing options...
Staggan Posted June 6, 2014 Author Share Posted June 6, 2014 Thank you... And sorry, I misread your original answer. I wanted to understand the reasoning, which you have now explained, so I will have to bite the bullet and amend the database to store the tokens for validation. Thanks again. Quote Link to comment Share on other sites More sharing options...
Jacques1 Posted June 6, 2014 Share Posted June 6, 2014 OK. Quote Link to comment Share on other sites More sharing options...
paddy_fields Posted June 7, 2014 Share Posted June 7, 2014 Jaques1, my site with tokens hasn't gone live yet... thankfully... as I completely forgot to hash the token! Lifesaver! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.