0

This is being used for a personal website, so it's more a learning experience than being super secure. That said, I've been reading various things you're supposed to do, and to me at least, this seems pretty secure, despite perhaps the low memory usage.

However, everywhere I read says never try do it yourself, so obviously there will be some flaws with my way. Could anyone suggest any improvements? For the record I was trying to do it with libraries that were built into the free webhosts, since both PBKDF2 and bcrypt weren't in.

The salt is generated via substr(uniqid(mt_rand(), true), 0, 32) (substr to avoid it overflowing the database occasionally it ends up as 33 characters), and encrypted before being entered into the database. This is via a hash of a global key, so you'd also need the contents of a php file to crack it (I may switch it to 'global key + email' though).
The IV is just stored inside a folder, so even if you hack the database and get the contents of a php file, you still need to get past htaccess to read a file. This can be deleted, where the next run of the function will generate a new one, and therefore render all passwords invalid.

The actual password hash uses key stretching, but to a value that's a modulus of a crc32 value, so each user has a different amount of hashes that are done. Each repeat hash uses a few hashes inside it to try keep it unique.

$salt_key = 'seemingly random string of characters, numbers and symbols';

function get_iv(){
    // It stores it as a file, but there's no point showing that part so here's the actual code bit
    return mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_CBC), MCRYPT_RAND);
}

function generate_salt(){
    global $salt_key;
    $salt = substr(uniqid(mt_rand(), true), 0, 32);
    $key = hash('sha256', $salt_key, true);
    $iv = get_iv();
    $encrypt = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $salt, MCRYPT_MODE_CFB, $iv);
    return array($salt, $encrypt);
}

function decrypt_salt($encrypted){
    global $salt_key;
    $key = hash('sha256', $salt_key, true);
    $iv = get_iv();
    return mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $encrypted, MCRYPT_MODE_CFB, $iv);
}

function hash_password($password, $salt){
    #return hash('sha512', $password.$salt, true);
    $hash = $salt;
    for($i=0; $i<(crc32($salt.$password) % 129197); $i++){
        $hash = hash('sha512', $hash.$password.$salt.hash('sha512', $salt.$hash.$password).md5($salt.md5($password.$hash)), true);
    }
    return $hash;
}

There are a couple of other functions but they're mainly for checking the entered password against the one in the database. Are there some important parts I've missed, or for a small scale site would this probably be alright?

Also, quick question about key stretching, if I make it take half a second to calculate, doesn't that mean if the site suddenly gets 500 people trying to log in, someone will have to sit there for 250 seconds because the CPU is overloaded?
I just had an idea to store the max value of hashes (129197 in the above code) in the user table, so it can be changed if needed without invalidating the passwords.

Peter
  • 3,186
  • 3
  • 26
  • 59
  • 3
    To improve it, I'd say dump it. There is a [password API](http://php.net/password) built into PHP. Use that, don't roll your own when it comes to security. – Jonnix Oct 10 '16 at 10:42
  • 1
    Don't roll your own password hashing. That's what you can do to improve it. – Andrei Oct 10 '16 at 10:43
  • The PHP version is below 5.5 though so there's no `password_hash` function – Peter Oct 10 '16 at 10:45
  • The function you have written is great and it will work fine for small scale and it's great for understanding how things work. But still you should use the password API as @JonStirling says. – Suraj Oct 10 '16 at 10:46
  • 1
    @Peter Use [this](https://github.com/ircmaxell/password_compat) for lower versions of PHP – Andrei Oct 10 '16 at 10:47
  • Thanks, I actually just came across that myself haha, I'll switch it if I ever end up making an actual website, but as I mentioned, it's currently just for a personal website so it's more of a learning experience doing it manually :) – Peter Oct 10 '16 at 10:49
  • I'd suggest taking it over to http://security.stackexchange.com/ (I think). It's not really on-topic for SO. – Jonnix Oct 10 '16 at 10:49
  • You shouldn't use anything lower than 5.6 anyway [because it's all dead](http://php.net/eol.php). Asking about security while using an unsupported piece of software is just silly. – PeeHaa Oct 10 '16 at 10:57
  • Also if it is for a "learning experience" don't use it in any website. Either personal or not. – PeeHaa Oct 10 '16 at 10:59
  • I'll have a look into getting 5.6 if possible, and just a quick question about `password_hash`, is it really secure even if I don't do anything extra? `password_hash('some password', PASSWORD_BCRYPT, $options)`, where options has cost and salt seems a bit too easy to be true. – Peter Oct 10 '16 at 11:06
  • Forget about the salt option. The addition of that option was a mistake and has been corrected in later PHP versions. And yes it's that easy, because that was the goal of the API. – PeeHaa Oct 11 '16 at 10:20

0 Answers0