Hey everyone,
In keeping with the current stream of entries, this one is also about a new feature in PunBB 1.3: the improved password hashing implementation.
First though, it’s worth taking a look at how PunBB’s password hashing system currently works. PunBB 1.2 uses a function called pun_hash to decide which hashing algorithm to use. The function chooses SHA-1 if it is available: otherwise, it chooses MD5. The passwords stored in the database are simply hashes of the plaintext passwords: there is no salt added to the hashes. The values stored in cookies are MD5 hashes of password hashes from the database combined with the sitewide $cookie_seed. In case my explanation confused you, I’ve written a couple code examples below.
A password stored in the database is created by
pun_hash($plaintext_password)
and a cookie password hash is created by
md5($cookie_seed.pun_hash($plaintext_password))
1.2’s implementation is good, but it has several places it can be improved:
- There is no salting done in the database, which means that any passwords grabbed directly from the database (via SQL inject, etc) are more vulnerable to attacks using rainbow tables.
- Everyone’s cookie hash uses the same salt. Thus, a malicious user could take his/her cookie hash and his/her password (which he/she knows, obviously) and use brute force to find the value of $cookie_seed. Once the malicious user has that value, his/her brute force attacks just became simpler to do (although the malicious user would still need to steal another user’s cookie value via XSS or some other method).
So, how does 1.3 improve upon 1.2’s password security?
- PunBB 1.3 requires SHA-1 hashing. The pun_hash function has been eliminated as a result. But have no fear, any passwords stored as MD5 are “upgraded” when the user logs in the first time.
- The sitewide $cookie_seed has been abandoned in favor of a new feature I will talk about in just a few seconds. $cookie_seed added relatively little security overall and there was no real need to keep it hanging around.
- All rows in the users table now have a new column, salt. That column stores a 12 character long salt which is randomly generated for each user. That’s right, PunBB now has per-user salts!
To go a little more in depth with #3, the salt is made up of 12 characters which have ASCII values between 33 and 126 (inclusive). That salt is used when storing the password in the database and when generating the cookie password hash (they are now exactly the same value). So, the code to generate a password hash now looks something like:
sha1($user_salt.sha1($plaintext_password))
where $user_salt is the randomly generated salt that each user has.
Some of the benefits of this new implementation:
- 2 users with the same plaintext password should not have the same hashed password (unless they randomly are given the same salt). In addition, a user who uses the same password on 2 user accounts will have 2 different password hashes (again, assuming the user does not randomly get identical salts).
- Because every password is salted and hashed in the database, it’s impossible for a malicious user to launch an attack against all passwords in the database at the same time. Each user’s password would have to be brute forced individually.
- There are only per-user salts, so a malicious user who brute forces his/her own hashed password will just get his/her per-user salt, which isn’t useful for breaking the passwords of others.
Of course, even with this wonderful salting, there is no substitute for a good, strong password. However, that’s a bit beyond the scope of this entry: if people are interested in learning some guidelines for secure passwords, they can read some of this topic.
~ Smartys
On password, I think it would be good to have it stored in a large string (so one can use a passphrase and not only a password), and allowing any unicode character of course.
And even more important, let the user know it on the registration page, and on the change-password page. Nothing too fancy, just a couple of sentences explaining that he can use long password/phrase, can and should use lower and uppercase letter, number, punctuations and others glyphs from any language.
February 21st, 2007, at 11:33 pm #Passwords are stored as SHA-1 hashes, so they can be as long as the user desires. However, I just took a look and I noticed some language strings are talking about a 16 character password limit (a limit which isn’t enforced within the code itself). I’ll look into getting that fixed.
I’ll also look into getting more descriptive about the types of passwords allowed.
Thanks Jérémie! :)
February 22nd, 2007, at 12:06 am #and what about servers with no SHA-1 hashing ?
February 22nd, 2007, at 10:44 am #PunBB 1.3 requires PHP 4.3 or later which means we can always rely on SHA1 being available.
February 22nd, 2007, at 10:46 am #_Just saw 902&903_ , thanks Neal :)
February 23rd, 2007, at 12:19 am #Keeping in mind point 1 from version 1.2’s implementation, do the per-user salts increase security if SQL injection is possible? Doesn’t either implementation depend on whether or not SQL injection is possible? Is the new method inherently more secure than the old one (regardless of SQL injection capability)? Thanks.
February 26th, 2007, at 5:57 pm #Chris: You have a couple points, so lets see if I can address them.
“Keeping in mind point 1 from version 1.2’s implementation, do the per-user salts increase security if SQL injection is possible?”
They can’t use precomputed rainbow tables. So, it’s slightly better (we’re talking about individual passwords, not brute forcing multiple passwords: that’s a good deal harder because you need to redo your work for each password/salt combo. I’m also assuming that the salt is known as well).
“Doesn’t either implementation depend on whether or not SQL injection is possible?”
An SQL injection does make cracking the password somewhat easier, but it is a step up in security.
“Is the new method inherently more secure than the old one (regardless of SQL injection capability)?”
February 26th, 2007, at 8:49 pm #Yes. Per-user salts are more secure than site-wide salts and no salts.
How hard is it to point another php script (dadabik) to use the punbb db for authentication? Anyone done something similar ?
March 9th, 2007, at 1:33 pm #rootuid: That would depend on the script. You’d be better off asking the author(s) of the script you want to integrate and/or in the Integration section of the PunBB support forums.
March 9th, 2007, at 10:08 pm #Ok, I just stumbled upon this while researching a vulnerable site. And I’ll have to say that this password hashing is highly inadequate.
The problem is that you are sticking a hash in a cookie that is not tied to a specific machine in any way. If a site has an XSS vulnerability anywhere on it (like the site I was researching), I can trivially steal a user’s punbb_cookie, and then insert it into my own browser, and suddenly I am logged in as them.
You definitely need to re-think this scheme. At the very least, you should be including the IP address of the user in the hash. As it stands, it is incredibly fragile and potentially vulnerable.
September 16th, 2007, at 9:47 pm #WAHa.06×36: Interesting point and certainly one worth thinking about.
There is one potential mitigation in that PunBB uses HttpOnly cookies. However, those are only respected by IE (and Firefox, if the user has installed an extension to add support for HttpOnly). We can only hope that it is more widely adopted in the future (I believe Firefox 3 is supposed to support it).
I’d argue that the password hashing is more than adequate, since it does protect the actual password. As you pointed out though, XSS is an issue that the new system does not deal with as well as it potentially could. However, I would make two arguments:
1. Adding the user’s IP to the hash would mean that, for example, AOL users using the browser inside the AOL software would be unable to stay logged in (it’s my understanding that AOL still shifts its users between various servers every few requests or so)
2. If you can inject Javascript to steal the cookie silently, you probably can also inject Javascript that would make the user submit the request for you, which would render the IP in the hash useless.
I will bring up your point with the other developers and we’ll see if we can come up with a better solution. Thanks for commenting!
September 18th, 2007, at 1:47 am #WAHa.06×36: The dilemma is hardly unique. All cookie based systems that I know of suffer from similar weaknesses. Like Neal said, baking in the current remote IP is not a solution since it would break every time a user got a new IP (which can happen very frequently depending on how you connect to the Internet).
I’d love to hear any other suggestions you might have though.
September 18th, 2007, at 9:03 am #I’ve been told the HttpOnly cookies don’t work on older PHPs, either. It certainly wasn’t working on the site I was testing on.
Unfortunately, the only method that is in any way effective that I can think of is tying to an IP. LiveJournal makes this an option when logging in. That is one solution, as long as you keep it on by default and tell people who run into trouble to deselect it. Certainly not ideal, but a lot more secure, as it works for most people.
You could bake in some other details, like the User-Agent, to make things trickier, but that’s pretty easy to defeat too.
And yeah, the fact that you just hijack the user’s browser and submit requests for them is *also* a problem. For that one, you might need to do stuff like checking Referers.
September 18th, 2007, at 6:46 pm #“I’ve been told the HttpOnly cookies don’t work on older PHPs, either. It certainly wasn’t working on the site I was testing on.”
They’re not implemented as a feature of setcookie before 5.2, but it’s possible to add HttpOnly to the setcookie call.
“Unfortunately, the only method that is in any way effective that I can think of is tying to an IP. LiveJournal makes this an option when logging in. That is one solution, as long as you keep it on by default and tell people who run into trouble to deselect it. Certainly not ideal, but a lot more secure, as it works for most people.”
But “works for most people” is not good enough (and I’m not sure how you define “most,” there are quite a few AOL users for starters). Also, anyone with a dynamic IP will have the “save my login to this computer” option fail at some point, and I’m pretty certain that people with static IPs are a minority.
The extension system should allow someone to add this functionality if they want, though.
“And yeah, the fact that you just hijack the user’s browser and submit requests for them is *also* a problem. For that one, you might need to do stuff like checking Referers.”
September 18th, 2007, at 7:03 pm #Which you can fake using Javascript in many browsers. To be honest, if you can execute arbitrary Javascript, there’s not much that can be done to protect the user.
HttpOnly cookies in PHP <5.2: http://dev.punbb.org/changeset/587
“And yeah, the fact that you just hijack the user’s browser and submit requests for them is *also* a problem. For that one, you might need to do stuff like checking Referers.”
I’m not entirely sure what you’re referring to here, but if you’re talking about CSRF attacks, you should read this: http://blog.punbb.org/2007/09/18/preventing-csrf-attacks/
September 18th, 2007, at 9:41 pm #