This question already has answers here:
Is "double hashing" a password less secure than just hashing it once?
(16 answers)
Closed 9 years ago.
I've read many posts on SO on how you should implement password hashing. And I've read that you shouldn't hash the password many times (well, it doesn't help much, it is said). But why not? If I iterate the hashed passwords, let's say, 10,000,000 times (because user can wait 3 seconds to have his registration completed, or I could just do that by sending an AJAX request).
So, how an attacker, stolen my database, and even knowing that I just iterate the password 10,000,000 times (worst-case scenario), could possibly find out users' passwords? He couldn't create a rainbow table, as it would take him very long (hashing passwords takes time, and hashing the hash so many times takes much more time), brute-force is also not really possible, so what's left?
evening: I wasn't saying anything about bcrypt or PBKDF.
Your question implicitly screams "I am trying to kludge my way around having to use bcrypt/PBKDF by poorly imitating their methods". However, the problems raised in the duplicate question are the reason why these new algos were devised instead of simply re-hashing a key X times.
You want a simple answer? Then: yes. X+1 hashing rounds are more secure than just X hashing rounds, but only marginally so. You might spend a second or two computing the hash on your server by looping over $hash = hash('sha512', $hash); But an attacker is going to use the Slide Attack to cut that down to a fraction of the time, and on top of that they're likely going to parallelize the attack across a few AWS instances, a farm of graphics cards, or a botnet.
The methods that PBKDF and bcrypt employ go quite a ways towards minimalizing/negating the effect of the slide attack, and bcrypt does some sort of magic voodoo that prevents it from being parallelizable to some extent.
Because of exist slide attack, which independent of number of cypher/hash rounds. See:
http://en.wikipedia.org/wiki/Slide_attack
Because of MD5's way of encoding it always outputs a string with the same length (32 characters for instance). In essence this means that a string with "I am a string" potentially can have the same hash as the "Whooptydoo" string, although this is a very (to the power of 100) small chance, it is still a chance.
This also means that repeat calculating the hash on your string a X number of times doesn't change the probability of it being cracked, as it doesn't encode it more deeply then it already was.
I hope I explained it clear enough, please comment if I have missed something.
I'm assuming you are talking about hashing the password, and then hashing the hash, hashing THAT hash, etc. You could easily then create a rainbow table that just maps hash values to the hash-of-hash values.
So, if HASH(PASSWORD) = H, and then HASH(H) = H1, HASH(H1) = H2, and so forth, then an easily downloadable rainbow table could contain a list like PASSWORD | H and reverse lookup the password that way. Now just, in addition, have a file that looks like H | H10000 and reverse that as well.
So, you've inconvenienced a would be hacker, maybe. But it's not really "more secure" because it's just a longer road, not really a more treacherous or difficult one.
Related
I need to know if exists any form to get a unique hash from gif images, i did tried with SHA1 file function
sha1_file
but i don't know if exist the case where two hash of different gif images, result in same hash.
Its can happen with SHA1? In this case is better SHA2, or MD5? Or any other previously implemented in PHP language.
I know its also depends of file size, but gifs image don't exceed 10mb in any case.
I need recommendations for this problem. best regards.
There is no hash function that creates different values for each and every set of images you provide. This should be obvious as your hash values are much shorter than the files themselves and therefore they are bound to drop some information on the way. Given a fixed set of images it is rather simple to produce a perfect hash function (e.g. by numbering them), but this is probably not the answer you are looking for.
On the other hand you can use "perfect hashing", a two step hashing algorithm that guarantees amortized O(1) access using a two step hashing algorithm, but as you are asking for a unique 'hash' that may also not be what you are looking for. Could you be a bit more specific about why you insist on the hash-value being unique and under what circumstances?
sha1_file is fine.
In theory you can run into two files that hash to the same value, but in practice it is so stupendously unlikely that you should not worry about it.
Hash functions don't provide any guarantees about uniqueness. Patru explains why, very well - this is the pigeonhole principle, if you'd like to read up.
I'd like to talk about another aspect, though. While you won't get any theoretical guarantees, you get a practical guarantee. Consider this: SHA-256 generates hashes that are 256 bits long. That means there are 2256 possible hashes it can generate. Assume further that the hashes it generates are distributed almost purely randomly (true for SHA-256). That means that if you generate a billion hashes a second, 24 hours a day, you'll have generated 31,536,000,000,000,000 hashes a year. A lot, right?
Divide that by 2256. That's ~1060. If you walked linearly through all possible hashes, that's how many years it would take you to generate all possible hashes (pack a lunch). Divide that by two, that's... still ~1060. That's how many years you'd have to work to have a greater than 50% chance of generating the same hash twice.
To put it another way, if you generate a billion hashes a second for a century, you'd have a 1/1058 chance of generating the same hash twice. Until the sun burns out, 1/1050.
Those are damn fine chances.
I am making a classified ads site with Zend Framework (for portfolio purposes, yes I know the world doesn't have room for "yet another Craigslist clone"). I am trying to implement the ability to post/edit/delete without ever needing an account.
To do this, I feel like I need to have a Nonce generated upon post submission and stored in the database. Then email a link to the user which makes a GET request for the delete, like this:
http://www.somesite.com/post/delete/?id=123&nonce=2JDXS93JFKS8204HJTHSLDH230945HSLDF
Only the user has this unique key or nonce, and upon submission I check the database under the post's ID and ensure the nonce matches prior to deleting.
My issue is how secure the nonce actually is. If I use Zend Framework's Zend_Form_Element_Hash, it creates the hash like this:
protected function _generateHash()
{
$this->_hash = md5(
mt_rand(1,1000000)
. $this->getSalt()
. $this->getName()
. mt_rand(1,1000000)
);
$this->setValue($this->_hash);
}
In reading about mt_rand(), one commenter said "This function has limited entrophy. So, if you want to create random string, it will produce only about 2 billion different strings, no matter the length of the string. This can be serous security issue if you are using such strings for session indentifiers, passwords etc."
Due to the lifetime of the nonce/token in the application, which could be days or weeks before user chooses to delete post, I think more than enough time would be given for a potential hack.
I realize mt_rand() is a huge upgrade from rand() as seen in this visual mapping pixels with rand on the left, and mt_rand on the right. But is it enough? What makes "2 billion different strings" a security issue?
And ultimately, how can I increase the entropy of a nonce/token/hash?
For such security it's not only important how long your output is. It counts how much randomness you've used to create it.
For mt_rand() the source of randomness is its seed and state (number of times you've used it since it was seeded). More mt_rand() calls will just give you more rehasing of the same randomness source (no new entropy).
mt_rand()'s seed is only 32-bit (anything less than 128bit makes cryptographers suspicious ;)
Strength of a keys with 32-bits of entropy is 4 billion divided by (roughly) number of keys you'll generate (e.g. after 100K uses there will be ~1:43000 chance to guess any valid key, which approaches practical brute-forcing).
You're adding salt to this, which makes it much stronger, because in addition to guessing the seed attacker would have to know the salt as well, so if the salt is long, then overall the key may be quite strong despite "low" entropy.
To increase entropy you need to add more random stuff (even slightly random is OK too, just gives less bits) from different sources than mt_rand: microtime(), amount of memory used, process ID... or just use /dev/random, which collects all entropy it can get.
(edit: uniqid() has weak entropy, so it won't help here)
The Zend hash generating code above's input for the md5() hashing function has 1,000,000 X 1,000,000 different possibilities. md5() has 32^16 (1208925819614629174706176) possible outcomes no matter what the input is. On average, the hacker would need to send 500,000,000,000 requests to your server in order to guess the right nonce.
At 100 requests per minute, that's about 3472222 days to hack.
I'm looking at some code that I have not written myself. The code tries to hash a password with SHA512 and uses just time() as the salt. Is time() too simple a salt for this or is this code safe?
Thanks for the answers and comments. I will sum it up here for the new readers:
salt should be different for each user, so if 2 users register at the same time, their salts won't be unique. This is a problem, but not a big one.
but salt shouldn't be in any way related to the user, so time() is not a good salt.
"Use a random, evenly distributed, high entropy salt." -- That's a mouthful, so what code could possibly generate a random, evenly distributed, high entropy salt?
Ok, so how about I replace time() with a random string 32 char long. The random string could be generated from looping 32 times over a set of alphabet chars. Does that sound good?
Short answer:
No, time() is not a good salt.
Long answer:
copied from my answer to Salt Generation and open source software
What is a salt?
A salt is a random set of bytes of a fixed length that is added to the input of a hash algorithm.
Why is salting (or seeding) a hash useful?
Adding a random salt to a hash ensures that the same password will produce many different hashes. The salt is usually stored in the database, together with the result of the hash function.
Salting a hash is good for a number of reasons:
Salting greatly increases the difficulty/cost of precomputated attacks (including rainbow tables)
Salting makes sure that the same password does not result in the same hash.
This makes sure you cannot determine if two users have the same password. And, even more important, you cannot determine if the same person uses the same password across different systems.
Salting increases the complexity of passwords, thereby greatly decreasing the effectiveness of both Dictionary- and Birthday attacks. (This is only true if the salt is stored separate from the hash).
Proper salting greatly increases the storage need for precomputation attacks, up to the point where they are no longer practical. (8 character case-sensitive alpha-numeric passwords with 16 bit salt, hashed to a 128 bit value, would take up just under 200 exabytes without rainbow reduction).
There is no need for the salt to be secret.
A salt is not a secret key, instead a salt 'works' by making the hash function specific to each instance. With salted hash, there is not one hash function, but one for every possible salt value. This prevent the attacker from attacking N hashed passwords for less than N times the cost of attacking one password. This is the point of the salt.
A "secret salt" is not a salt, it is called a "key", and it means that you are no longer computing a hash, but a Message Authentication Code (MAC). Computing MAC is tricky business (much trickier than simply slapping together a key and a value into a hash function) and it is a very different subject altogether.
The salt must be random for every instance in which it is used. This ensures that an attacker has to attack every salted hash separately.
If you rely on your salt (or salting algorithm) being secret, you enter the realms of Security Through Obscurity (won't work). Most probably, you do not get additional security from the salt secrecy; you just get the warm fuzzy feeling of security. So instead of making your system more secure, it just distracts you from reality.
So, why does the salt have to be random?
Technically, the salt should be unique. The point of the salt is to be distinct for each hashed password. This is meant worldwide. Since there is no central organization which distributes unique salts on demand, we have to rely on the next best thing, which is random selection with an unpredictable random generator, preferably within a salt space large enough to make collisions improbable (two instances using the same salt value).
It is tempting to try to derive a salt from some data which is "presumably unique", such as the user ID, but such schemes often fail due to some nasty details:
If you use for example the user ID, some bad guys, attacking distinct systems, may just pool their resources and create precomputed tables for user IDs 1 to 50. A user ID is unique system-wide but not worldwide.
The same applies to the username: there is one "root" per Unix system, but there are many roots in the world. A rainbow table for "root" would be worth the effort, since it could be applied to millions of systems. Worse yet, there are also many "bob" out there, and many do not have sysadmin training: their passwords could be quite weak.
Uniqueness is also temporal. Sometimes, users change their password. For each new password, a new salt must be selected. Otherwise, an attacker obtained the hash of the old password and the hash of the new could try to attack both simultaneously.
Using a random salt obtained from a cryptographically secure, unpredictable PRNG may be some kind of overkill, but at least it provably protects you against all those hazards. It's not about preventing the attacker from knowing what an individual salt is, it's about not giving them the big, fat target that will be used on a substantial number of potential targets. Random selection makes the targets as thin as is practical.
In conclusion:
Use a random, evenly distributed, high entropy salt. Use a new salt whenever you create a new password or change a password. Store the salt along with the hashed password. Favor big salts (at least 10 bytes, preferably 16 or more).
A salt does not turn a bad password into a good password. It just makes sure that the attacker will at least pay the dictionary attack price for each bad password he breaks.
Usefull sources:
stackoverflow.com: Non-random salt for password hashes
Bruce Schneier: Practical Cryptography (book)
Matasano Security: Enough with the Rainbow Tables
usenix.org: Unix crypt used salt since 1976
owasp.org: Why add salt
openwall.com: Salts
Disclaimer:
I'm not a security expert. (Although this answer was reviewed by Thomas Pornin)
If any of the security professionals out there find something wrong, please do comment or edit this wiki answer.
As for what seems to be a good source for your random salt
Also read: What is the most secure seed for random number generation?
In the absence of dedicated, hardware based, random generators, the best way of obtaining random data is to ask the operating system (on Linux, this is called /dev/random or /dev/urandom [both have advantages and problems, choose your poison]; on Windows, call CryptGenRandom())
If for some reason you do not have access to the above mentioned sources of random, in PHP you could use the following function:
From the source of phpass v0.3
<?php
/**
* Generate pseudo random bits
* #copyright: public domain
* #link http://www.openwall.com/phpass/
* #param int $length number of bits to generate
* #return string A string with the hexadecimal number
* #note don't try to improve this, you will likely just ruin it
*/
function random_bits($entropy) {
$entropy /= 8;
$state = uniqid();
$str = '';
for ($i = 0; $i < $entropy; $i += 16) {
$state = md5(microtime().$state);
$str .= md5($state, true);
}
$str = unpack('H*', substr($str, 0, $entropy));
// for some weird reason, on some machines 32 bits binary data comes out as 65! hex characters!?
// so, added the substr
return substr(str_pad($str[1], $entropy*2, '0'), 0, $entropy*2);
}
?>
Updated
It's not a really good salt, but probably good enough to defeat all but the most determined and resourceful attackers. The requirements for a good salt are:
Different for each user
long enough (at the very least alphanumeric 8 characters) to make the concatenation of salt and (potentially weak) password too long for a brute force attack.
time() values are not really long enough, since they have 10 characters, but only digits.
Also, sometimes two users may get the same value when they are created within the same second. But that's only a problem if you have situations where many users are automatically created within the same second.
In any case, far more important than a perfect salt is using a good hash function, and SHA512 is one of the best we have available right now.
This post may veer a little too far away from your original question, but I hope you find it useful;
Security is about raising barriers and hurdles; defence in depth. There is no truly secure hashing solution, just ones that are hard to break. It's like putting in a burglar alarm and window locks in your house - make your site less attractive to break into than someone else's.
Salt for a crypt algorithm is only a small part of the security problem. A single salt simply means that there is one less thing to figure out when trying to break the password for multiple users. A low-entropy salt (such as the server's time) makes it a little bit harder, and a high-entropy salt makes it harder still. Which of these to use, and whether it's something you need to worry about primarily depends upon both the sensitivity of the data you're protecting, but also what other security measures you have in place. A site that just gives a personalised weather forecast for a selected city obviously has less sensitive data than one which has your home address, mother's maiden name, date of birth and other info which could be used for identification purposes.
So here's the rub; a high entropy salt is still a bad salt if it's easily obtainable.
In the real world, storing a salt in the database (random or not) is probably less secure than using a constant salt and burying it away from private eyes in a file inaccessible via the web browser. Whilst a unique and high entropy salt is harder to guess, if you've allowed root login from any server on MySql and set the password to 'password' it doesn't really matter! Constrast how easy it is to crack the database versus getting a valid login to your server - which is possibly more difficult to do discretely as you can put fail2ban and a plethora of other attack vector watchers in place depending upon your setup.
You can combine the two approaches by storing the location of a file containing a user-specific salt in the database, rather than the salt itself. Whether having to crack both the file system and the database is warranted depends whether the sensitivity of the data you are trying to protect warrants this overhead.
Another, alternative, recommendation from security experts is to store the username in a separate database (and ideally different technology) to the password, and reference between the two using a UUID. E.g. use both MySQL and SQLite. This means that both databases have to be cracked (and is also why, to go down a separate rabbit hole for the sake of an example, you should not store user details and credit card numbers in the same database since one is of no use without the other).
Note that Algorithms like SHA-512 and Blowfish can return the salt as part of their hash. Be careful with these as if you store the complete hash you give away the algorithm, which means there's two less thing for the hackers to figure out (the salt also gives away the algorithm).
Make sure you enforce strong passwords and usernames, so dictionary attacks will fail; I know of dictionaries for all 6-alphanumeric combinations of username/ password entries for MD5 and I suspect that there are more than this available for all sorts of algorithms. With the explosion of low-cost cloud and CPGPU computing, the size and complexity of available dictionaries is going to explode.
Ultimately, the most secure way is never to programatically generate a salt but require a user to enter it along with their username and password over a SSL link (so can't be snooped), but never store it. This is the approach taken by credit card companies; i.e. the 3-digit CSV security key on your credit card which you have to enter each and every time you buy online, since it should never be stored in any database. If you really want to generate the salt, send it to them separately (e.g. via SMS message or Email) and still make them enter it manually each time. With this approach, although more secure, you need to contrast the complexity against whether users will just stop using the site as you've made it too difficult for them to be bothered with it.
All of the above still relies on the fact that you also have protection in place against session hijacking, cross-site scripting, etc., etc. The world's strongest password algorithm is irrelevant if all I need to do is to calculate a valid PHPSESSID for a logged-in user and hijack it!
I am not a security expert, but have read up on this as much as I reasonably can do. The fact that there are so many books on the subject indicates how big the answer to your question really is.
A couple of really great books you might like to try which I've found invaluable are;
Web Application Vulnerabilities Detect, Exploit, Prevent - ISBN-13: 978-1-59749-209-6
Preventing Web Attacks with Apache - ISBN-13: 978-0-321-32128-2
No, time() is not a good salt
It's best not to reinvent the wheel when it comes to authentication, but to answer your question, no. The problem with time():
It's predictable and it correlates to potentially discoverable things. These issues make it easier to cross-match different hashed results.
There aren't very many possible values. Since the high-order bits don't change, it's an even narrower salt than it first appears.
Using it repeats previous mistakes. If this app were the first one to use time() as a salt, at least it would require a new attack.
Yes.
It seems that a unix timestamp, stored in the user database as a "Member since" field going to be decent salt.
However, salt question is most negligible one.
There are much more important things you have to pay attention to:
Most likely not a password nor salt or hashing algorithm going to be weakest part of your site. Some lame file injection or XSS or CSRF surely is. So, don't make a too big deal of it.
Speaking of a true random string of 32 char long in the typical web-application is like speaking about 32-inch armored door in the wooden barn.
Speaking of passwords, most ever important thing is password complexity. With weak password no salt nor hashing algorithm, even super-ingenious-incredible-hard one, could help. It's a pain to ask users to use complex password, but without it everything else becomes a piece of crap.
So, your first concern should be password complexity. 12-16 characters of different case, including numbers and punctuation is a requirement.
As for the salt, I see no benefit in using time, as you have to store it along with other user data. Better use a email - it's random enough and you have it already anyway. Don't forget to rehash a password if user changes their email. it seems that unix timstamp going to be a decent salt, no need to use email or anything else.
Update
As I can see, many people still unable to get the point.
Like that guy from the comments, saying
Many users use weak passwords (we should educate them, or at least keep trying), but that is no excuse; they still deserve good security
They deserve, no doubt. But with weak password the mission. is. impossible.
If your password is weak, then no salt will protect it.
While salt is not that important to spend a 10-kilobyte text on the topic.
Salt is use to prevent rainbow attacks by breaking the match between the password and precomputed hash. So the main task for a salt is to be different for each user/password record. Quality of randomization of the salt doesn't matter much as long as the salt is different for different users.
the date when a member joins a forum/website is generally openly access able , which would be same as time() hence making your salt useless.
No! Never use the current time as the salt. You can use something like 'SecureRandom' in java to generate a random salt that is secure. Always use an unpredictable random number as the salt. Using time as the salt will help you to remove collisions only upto a certain extent(because two users can sypply the same passwords at the same time), but still make the passwords recoverable.
The user name should be sufficient enough and perhaps the registration time stamp, but you should store it somewhere in the database. Anyway every value you use to salt your password hash, should be stored some way, so you can recalculate the hash.
Is salting with user name + a time stamp secure enough? It should be. For cracking SHA512 Hashes normally Rainbow Tables are used. A user name + a time stamp should be a salt which is uniquq enough, so there is no way there is some Rainbow Table on the net which contains precalculated hashes with passwords, which are salted this way.
I know MD5's safety is under question lately and this is the reason a lot of people are using salt (I dont understand this at all btw) but I was wondering if you wanted to easily implement a safe system in php can you just md5 something twice?
like test > 098f6bcd4621d373cade4e832627b4f6 > fb469d7ef430b0baf0cab6c436e70375
So basically:
$val = 'test';
$val = md5($val);
$val = md5($val);
Would that solve the whole rainbow security stuff? Is there an easy/noob proof way of making secure database passwords in php?
Hashing twice makes little real sense and doesn't accomplish much. In general, however, multiple hashing can make some sense. For example, if you hash enough times to take something like 100 ms (or so, depending on hardware) it can help a little. The basic idea of it is pretty simple: adding 100 ms to a normal login is a barely noticeable delay -- but if you're trying to build something like a table for a dictionary attack, multiplying the time by something like a thousand (or whatever exactly it works out to) starts to make a real difference -- a table that you could normally compute in (say) a day, takes a few years instead. That's enough difference that anything but really serious attackers will often give up (or just get bored) long before they finish the job.
Salt is an entirely separate tool. Using it does not make up for weakness in the underlying hash function. The idea here is that the size of a table for a dictionary attack becomes substantially larger (e.g., for a one-byte salt, 256 times larger). The salt is not normally kept secret, but it's relatively random, so an attacker who's doing a dictionary attack can't just hash each word as-is, but has to take each possible salt value into account. At the risk of repetition: it deals with a weakness in how (most) people pick passwords, not any weakness in the hash function itself.
If you don't believe in MD5, you can try a higher algorithm by using the hash() function:
$hash1 = hash('sha1', 'The string to hash by SHA-1');
$hash2 = hash('sha256', 'The string to hash by SHA-256');
$hash3 = hash('sha512', 'The string to hash by SHA-512');
$hash4 = hash('ripemd160', 'The string to hash by RIPEMD-160');
In my opinion it does not make sense to hash twice.
EDIT: Fixed typo in last line of code.
Whether or not you use the MD5 algorithm...
No, an attacker can always have two rainbow tables (one for the extra level of hashes, and one for the passwords). And from another answer of mine:
[...] it still just requires the password and nothing more to crack. In other words, you are just applying the hashing functions to the same thing a few times more.
You use a salt to make it more difficult for the attacker to get at your passwords, because then he would need to know the salt so that he can use it in computing the hashes for your passwords.
Storing passwords securely is tricky, most the advice posted here is not accurate. So I will defer to Thomas Ptacek's widely cited post on the subject: http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html
For the record, I evaluated that
$val = 'test';
$salt='somerandom!!aa##9900';
$val = md5($salt.$val);
$val = md5($val);
Its pretty safe. The secret is in the salt.
However, md5 is short so the chances of concurrences are "high" (one in 1.208.925.819.614.629.174.706.176 = 32^16, 32 words with an hexadecimal each one)
I have used unsalted md5/sha1 for long time, but as this method isn't really secure (and is getting even less secure as time goes by) I decided to switch to a salted sha512. Furthermore I want to slow the generation of the hash down by using many iterations (e.g. 100).
My question is whether I should append the salt on every iteration or only once at the beginning. Here are the two possible codes:
Append every time:
// some nice big salt
$salt = hash($algorithm, $salt);
// apply $algorithm $runs times for slowdown
while ($runs--) {
$string = hash($algorithm, $string . $salt, $raw);
}
return $string;
Append once:
// add some nice big salt
$string .= hash($algorithm, $salt);
// apply $algorithm $runs times for slowdown
while ($runs--) {
$string = hash($algorithm, $string, $raw);
}
return $string;
I first wanted to use the second version (append once) but then found some scripts appending the salt every time.
So, I wonder whether adding it every time adds some strength to the hash. For example, would it be possible that an attacker found some clever way to create a 100timesSha512 function which were way faster than simply executing sha512 100 times?
In short: Yes. Go with the first example... The hash function can lose entropy if feed back to itself without adding the original data (I can't seem to find a reference now, I'll keep looking).
And for the record, I am in support of hashing multiple times.
A hash that takes 500 ms to generate is not too slow for your server (considering that generating hashes are typically not done the vast majority of requests). However a hash that takes that long will significantly increase the time it will take to generate a rainbow table...
Yes, it does expose a DOS vulnerability, but it also prevents brute force attacks (or at least makes them prohibitively slow). There is absolutely a tradeoff, but to some the benefits exceed the risks...
A reference (more like an overview) to the entire process: Key Strengthening
As for the degenerating collisions, the only source I could find so far is this discussion...
And some more discussion on the topic:
HEKS Proposal
SecurityFocus blog on hashing
A paper on Oracle's Password Hashing Algorithms
And a few more links:
PBKDF2 on WikiPedia
PBKDF2 Standard
A email thread that's applicable
Just Hashing Is Far From Enough Blog Post
There are tons of results. If you want more, Google hash stretching... There's tons of good information out there...
In addition to re-hashing it multiple times, I would use a different salt for each password/user. Though I think 5000 iterations is a bit too much, try a lower number. There's a trade-off here; you'll have to tweak it according to your needs and hardware.
With different salts for each password, an attacker would be forced to bruteforce each password individually instead of constructing a rainbow table, which increases the workload considerably.
As always, here's a recommended read for this: Just hashing is far from enough
EDIT: Iterative hashing is a perfectly valid tactic. There are trade-offs, but everything has them. If you are worried about computation time, why not just store the plaintext password?
Please please please do not roll your own crypto. This is what libraries like OpenSSL are for. Here's few good examples of how you would use it to make salted hashes.
Salted Hashes in OpenSSL
The reason for iterative hashing is to make process as slow as possible. So you can do even better: use different salts for each iteration. It can be done by encrypting you original data again and again on each iteration with fixed key and XORing with salt value.
I prefer to go with a double sha1 with two different salts and prevent DoS delaying the answer incrementally (with a simple usleep) for every invalid password check.