Home > Questions > All Answers

All Answers

All Answers » Re: How do you use bcrypt for hashing passwords in PHP? » Comments for "Re: How do you use bcrypt for hashing passwords in PHP?"
  • Kevin
    Note this only works if you have PHP 5.3+ ! I have PHP 5.2.17 and get Error 500 due to the exception thrown if CRYPT_BLOWFISH is not available.

    NOTE: This comment was originally posted at StackOverflow.com by Ozzy

  • Valerie
    @Ozzy have a look at openwall.com/phpass which hass some fallback hashing schemes for 3 <= PHP < 5.3

    NOTE: This comment was originally posted at StackOverflow.com by jah

  • Tony
    Is there any significance to the crazy itoa64 vs. using base64_encode? The number of characters is the same, but the values are quite different. Is the former more secure somehow?

    NOTE: This comment was originally posted at StackOverflow.com by Explosion Pills

  • Jerry
    So, um, where do we store the salt?

    NOTE: This comment was originally posted at StackOverflow.com by Jeff Davis

  • Judith
    @JeffDavis the salt is an integral part of the generated hash already.

    NOTE: This comment was originally posted at StackOverflow.com by Andrew Moore

  • Rebecca
    @Ozzy You could also install the Suhosin patch to make CRYPT_BLOWFISH available on PHP 5.2.

    NOTE: This comment was originally posted at StackOverflow.com by Martijn Heemels

  • Bill
    @AndrewMoore so wait, the result of crypt($input, $this->getSalt()) is the same as crypt($input, $existingHash) where $existingHash is what we got from the result of the first thing? this really makes no sense to me how these end up the same

    NOTE: This comment was originally posted at StackOverflow.com by Andy Lobel

  • Judith
    @AndyLobel: The salt is present in the full hash, therefore the hash can be recalculated with the original salt to give out the same result. $2a$<roundCount>$<salt><computedHash>

    NOTE: This comment was originally posted at StackOverflow.com by Andrew Moore

  • Bill
    @AndrewMoore so in $existingHash the crypt function only uses the $2a$<roundCount>$<salt> ? and doesn't actually need the <computedHash> part? thanks for the reply ;-)

    NOTE: This comment was originally posted at StackOverflow.com by Andy Lobel

  • Ruth
    @AndrewMoore Hi! I'm trying to use this script, but I was wondering how do you check this against a stored hashed password in mysql? The has is always changing even if it is the same word. I'm just trying to figure out what is the best way to check this through mysql? Thanks!

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Ruth
    @AndrewMoore If I have an email/password login, I could probably check if email matches first and then get that associated password from the mysql row, and then check the verification process right?

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Bill
    @andrewliu it is always changed even when using the same word because a new random salt is generated each time when calling the hash() function making the overall result different each time

    NOTE: This comment was originally posted at StackOverflow.com by Andy Lobel

  • Ruth
    @AndyLobel I know that, thats what i said, I guess I misspelled hash. I'm asking what is the best way to check it inside a mysql database.

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Judith
    @andrewliu: Call the $bcrypt->verify() function with the plain text as first parameter and the hash stored in the database as second parameter.

    NOTE: This comment was originally posted at StackOverflow.com by Andrew Moore

  • Bill
    @andrewliu i do it the same way as you said, check the email and grab the hash from the database at the same time, then check that hash against the hash of the password entered by the user when logging in

    NOTE: This comment was originally posted at StackOverflow.com by Andy Lobel

  • Bill
    @AndrewMoore could you comment on the differences between this: yiiframework.com/wiki/292/secure-password-hashing-with-bcrypt and your one above and say which one is better, thanks ;-)

    NOTE: This comment was originally posted at StackOverflow.com by Andy Lobel

  • Judith
    @AndyLobel: the heavy lifting is done by crypt(). There is no better.

    NOTE: This comment was originally posted at StackOverflow.com by Andrew Moore

  • Ruth
    @AndrewMoore I like your idea of verifying it first. But the verification outputs a 1 if it is true? and it has two parameters? I'm a little confused by your method. Or am i thinking it differently? Thanks for responding!

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Ruth
    @AndrewMoore Also, I just tried my method, checking email, and then get the hashed password from the database to check the use verify(), and it seems like it still doesn't match. =\ help me pleaseeeeeee

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Judith
    @andrewliu: $bcrypt->verify($passwordAsProvidedByTheUser, $hashInTheDB); If it returns true, it is valid.

    NOTE: This comment was originally posted at StackOverflow.com by Andrew Moore

  • Bill
    @AndrewMoore the way of generating the bytes in the link i said is different to how you do it above; which way is better? sorrryy haha thats my last question

    NOTE: This comment was originally posted at StackOverflow.com by Andy Lobel

  • Bill
    @AndrewMoore ow right thanks, so as long as i have php version >= 5.3.0 and i'm not on a windows server then i can just get rid of the other fallbacks for generating bytes

    NOTE: This comment was originally posted at StackOverflow.com by Andy Lobel

  • Ruth
    @AndrewMoore yeah, that's exactly how I'm trying to check for verification. I've even tried to echo the verify(), but it doesn't come out as true or 1. I hashed the user input, then i checked the database with user email to get the "hashed" password from registration, and checked it through verify.

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Bill
    @andrewliu make sure the field in your database is long enough to hold the password hash varchar(60) i made that mistake for a while lol, and to check if its true use var_dump($bcrypt->verify('password', $hash))

    NOTE: This comment was originally posted at StackOverflow.com by Andy Lobel

  • Michael
    -1 because you're backup RNG is not random at all (based on only system time is not good randomness). Change it to use at least a vetted PRNG algorithm like MT.

    NOTE: This comment was originally posted at StackOverflow.com by ircmaxell

  • Judith
    @ircmaxell: mt_rand() is seeded exactly the same way in 5.3 (getmypid() is available on all systems... And you should know that, you are a ZCE after all). Arguably, the benefits for a cryptographically secure salt here is extremely small; in hashing, the salt is not considered a secret. It is simply there to force the attacker to generate a different rainbow table per password (instead of using the same for all). Being able to guess the next salt gives no benefit to the attacker (it's in plain text in the hash).

    NOTE: This comment was originally posted at StackOverflow.com by Andrew Moore

  • Ruth
    @AndyLobel at first i thought that would be the problem too, but i set my database field to text. ill try to use var_dump when I get a chance

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Ruth
    @AndyLobel yup, bool(false)

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Ruth
    @AndrewMoore Hi, I've actually made a question regarding my issue, I was wondering if you maybe able to spot something that I'm missing? I'm getting really desperate, and this is the only thing I need to do to go forth on my login page (stackoverflow.com/questions/11481199/…) Thank you so much!

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Bill
    @andrewliu theres nothing wrong with the syntax of your code, the only reason it wouldnt work was if $pass_l or $chk_pass aren't what they're suspose to be ;-) soz i keep commenting on stuff aimed at the other guy looool i can teamview with you to hhelp ya out if u wana ;p

    NOTE: This comment was originally posted at StackOverflow.com by Andy Lobel

  • Michael
    @AndrewMoore: I'm not saying it should be CS, but don't implement your own algorith. I know how mt_rand() is seeded. But it uses that seed as input to a strong algorithm. As opposed to this RNG which invents its own algorithm and constantly uses the time. What's wrong with the MT that compells you to write your own algorithm?

    NOTE: This comment was originally posted at StackOverflow.com by ircmaxell

  • Ruth
    @AndyLobel sorry, i forgot to add a variable $hash_1 which is the hash from $pass_1, and then its supposed to be checked with verify($hash_1, $chk_pass) stated from Andrew Moore from above comment.

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Ruth
    A guy answered it! Blah, looked like I was doing it wrong! Thanks!

    NOTE: This comment was originally posted at StackOverflow.com by andrewliu

  • Christopher
    @AndrewMoore, thanks for posting this code. It takes ~10 seconds on my server for the output to return true. Can you explain what part of the code is dedicated to the delay? I don't mind a millisecond delay but my application doesn't require this level of slowness.

    NOTE: This comment was originally posted at StackOverflow.com by tim peterson

  • Judith
    @timpeterson: How many rounds are you using? More rounds = more complexity = more time...

    NOTE: This comment was originally posted at StackOverflow.com by Andrew Moore

  • Christopher
    @AndrewMoore I literally copy and pasted your code into a test.php file. So I guess $rounds=12? However, when I decreased it to $rounds=1, it still took ~10 seconds. Is there something else I need to do to get time<1sec? For a consumer web application 10sec is too much time.

    NOTE: This comment was originally posted at StackOverflow.com by tim peterson

  • Anthony
    <strike>Am I the only one that doesn't see the rounds value being used in the hash function?</strike> Ignore that - it's specified as part of the hash.

    NOTE: This comment was originally posted at StackOverflow.com by Polynomial

  • Christopher
    @AndrewMoore, sorry, I now see where the $rounds=15 as specified in your code: $bcrypt = new Bcrypt(15);. If you change that to $bcrypt = new Bcrypt(4); then it returns true almost instantly.

    NOTE: This comment was originally posted at StackOverflow.com by tim peterson

  • Judith
    @timpeterson: You should select a number of rounds that results in 200-250 ms of work. Part of the reason why bcrypt is secure is that it is slow. You must ensure to have a number of rounds that keeps that characteristic.

    NOTE: This comment was originally posted at StackOverflow.com by Andrew Moore

  • Christopher
    @AndrewMoore thanks, running microtime() from Bcrypt(4)to Bcrypt(9) the time goes from 0.010 to 0.314. So Bcrypt(9) is what i'll probably do.

    NOTE: This comment was originally posted at StackOverflow.com by tim peterson

  • Brian
    Oh my god. Don't use crypto code that isn't uploaded to somewhere tied to, approved by, and peer-reviewed by people you can identify as real authorities on crypto. This isn't about open vs. closed source. Whereever it's uploaded to should offer visibility of the reviewed and verified source. This is about acknowledging that most of us don't have the chops to critique crypto, and not letting the blind lead the blind. I'm seriously supposed to rely on anonymous upvotes on a wiki to tell me whether I'm compromising my customer's data? Cause that's all non-crypto experts can do with this.

    NOTE: This comment was originally posted at StackOverflow.com by Michael Lang

  • Judith
    @MichaelLang: Good thing crypt() is peer-reviewed and verified then. The code above calls PHP's crypt(), which calls the POSIX crypt() function. All the code above does more is generating a random salt (which doesn't have to be cryptographically secure, the salt isn't considered a secret) before calling crypt(). Maybe you should do a little research yourself before calling wolf.

    NOTE: This comment was originally posted at StackOverflow.com by Andrew Moore

  • Brian
    @AndrewMoore Man, I'm sorry. I have been doing research. I just made a mistake and mistook your salt generating code for the hash creation. Maybe you could pull out the salt generation? I get really uneasy when I imagine people getting in the habit of copy/pasting a big pile of crypto code. It takes an attentive eye to distinguish what you did from the things that spawned this debate here: news.ycombinator.com/item?id=2654586

    NOTE: This comment was originally posted at StackOverflow.com by Michael Lang

  • Brian
    @AndrewMoore I'd edit the comment too, but the link is gone, so I can only delete (which I hate doing cause it makes replies nonsensical). But if you'd like a delete I'll do it

    NOTE: This comment was originally posted at StackOverflow.com by Michael Lang

  • Brian
    @AndrewMoore And now I know that bcrypt is opinionated about its salts, so nevermind on the salt generation comment. I'm not sure what to do with this, but trying to think about how to edit the answer to make it clear that when you call crypt with a salt constructed a particular way, you get a bcrypt hash. I'm busy checking my work though cause I want to get it right. A statement like "crypt with a fancy salt is bcrypt (as long as its supported)" is a lot easier to verify is BS or not than analyzing and researching why code is like it is.

    NOTE: This comment was originally posted at StackOverflow.com by Michael Lang