So far as what happens when the user clicks on the URL in the email, I think it's so simple it many never have been thought to make into a module. Your solution works fine, although here's how I tend to implement it:
I create a "temp" table, separate from the user table. Populate said table with the password the user chose, the one-off id field for the new user that's in the URL you emailed, and that user's ID in the "real" user's field (and, possibly, a timestamp). When your app sees the URL, it queries the "temp" table, and simply loads the real password into the correct row of the user table, and then deletes that entry in the temp table.
This provides you with an extra layer of security, and a way of cleaning up "false setups" without touching your user table (daily cron job to clean out x days old entries in "temp" column). It also means one less column to "drag" around for the user table; if it's queried often, lightweight is best, esp. if a field/column's to be used once.
Make sense?
----Asim, known to some as Woodrow.
| [reply] |
You just add a BOOL field "activated" to the user records, then in the email give them a url that contains:
(a) Their user name or ID
(b) A timestamp at which the URL expires
(c) A hash code based off their user name and/or password hash, the timestamp, and some internal site key
All you need for this is your standard database module and Digest::MD5.
Asim: I don't really see how making a separate table for just the authentication process is going to help much. Nobody's going to bother trying to brute-force an MD5 key, and if they're just spamming your site to shut it down (or because they're an automatic script), they're almost certainly going to spam the user signup rather than the user authentication. Either everything should be in one table for the sake of simplicity (and records accessed by ID) or the entire registration record should be in the temp table, and only moved to the primary table once it's been activated. | [reply] |
I don't really see how making a separate table for just the authentication process is going to help much. Nobody's going to bother trying to brute-force an MD5 key
Well, as I mention first off, his basic solution is Good Enough. What I'm saying is in part about security, true, and I'll touch more on that point in a moment. But I also find the idea of attaching an extra column, even a BOOL, for what amounts to a one-off to be a waste of space. In a table that's referenced many times a day, it's just one more thing to manage (unless you choose to leverage it into a level/auth system, and even then...) Once the user is verified, it's useless, and that's not my style, nor one I generally recommend. Call me The Normalizer, if you must. :)
You'll also note that I didn't mention MD5, or any secure hash routine, by name. I must call out one error that I did in editing the original response; the password stored in the Temp table should, indeed, be encrypted -- NO cleartext passwords! But I don't mention a scheme, and that's in part because MD5 is crackable in a non brute-force fashion, as is SHA-1. Now, in my opinion, you're right about MD5 being Good Enough for an authentication URL; it's not a huge deal for this guy's project, nor is SHA-1. But yea, the data that's actually stored in the DB for the password hash itself should be something stronger...and I confess that I didn't want to scare the petitioner half to death. However, I did what to give ideas, and hints on building a solid, flexible foundation for their auth scheme.
One more point, since you touch upon it. Most modern email clients can handle long URLs, no problem. Some, however, cannot, and some people are stuck on them, not to mention the potential of mangling. Because I've seen it happen, I firmly believe that the shorter the URL, the better off the end user is in terms of functionality. And the the less data you pass, esp. over GET, the better off you are in terms of internal security (see any paper on SQL injection for just one set of reasons why).
Either everything should be in one table for the sake of simplicity (and records accessed by ID) or the entire registration record should be in the temp table, and only moved to the primary table once it's been activated.
I like, and would employ, your 2nd solution if I have an application with many "false positives" -- people (or machines) registering, but never verifying. At that point, it becomes cleaner to keep your main user table tidy in that fashion, and I'm glad you brought it up as an option, as well.
----Asim, known to some as Woodrow.
| [reply] |
Well, mySQL translates bool as a byte field anyway, so you're adding a byte to each record - but who cares? Even with a million users, that's only one MB additional, and disk space is cheap. I know it bothers some people's delicate sensibilities, but I feel this is a bad argument for putting authentication in a whole separate table.
Am I correct that the MD5 hashes are only crackable if you can get your hands on a sample of the database contents, and can figure out what items are going into the hash? If so, it doesn't really matter much that MD5 is crackable, since if your database is open to intruders, your site is probably already doomed. All the intruder has to do is rewrite your login page to save user names and passwords to a file, or send fake authentication emails to people, or a wide variety of other methods.
Yes, long URLs can be a problem, and I suppose you could save the timestamp in the database and send only a user ID and hex hash key. I don't think injection is a major likelihood though if you only access the record based on user ID, and untaint that before passing it to the database. The only variable you're sending the database is an integer, which is easily verified. | [reply] |