• 2
Votes
name

A PHP Error was encountered

Severity: Notice

Message: Undefined index: userid

Filename: views/question.php

Line Number: 190

Backtrace:

File: /var/www/stackquestions.com/public_html/application/views/question.php
Line: 190
Function: _error_handler

File: /var/www/stackquestions.com/public_html/application/controllers/Questions.php
Line: 416
Function: view

File: /var/www/stackquestions.com/public_html/index.php
Line: 315
Function: require_once

name Punditsdkoslkdosdkoskdo

Secure hash and salt for PHP passwords

It is currently said that MD5 is partially unsafe. Taking this into consideration, I'd like to know which mechanism to use for password protection.

I'm using PHP. I want a safe and fast password encryption system. Hashing a password a million times may be safer, but also slower. How to achieve a good balance between speed and safety? Also, I'd prefer the result to have a constant number of characters.

  1. The hashing mechanism must be available in PHP
  2. It must be safe
  3. It can use salt (in this case, are all salts equally good? Is there any way to generate good salts?)

Also, should I store two fields in the database (one using MD5 and another one using SHA, for example)? Would it make it safer or unsafer?

In case I wasn't clear enough, I want to know which hashing function(s) to use and how to pick a good salt in order to have a safe and fast password protection mechanism.

I just want to point out that PHP 5.5 includes a password hashing API that provides a wrapper around crypt(). This API significantly simplifies the task of hashing, verifying and rehashing password hashes. The author has also released a compatibility pack (in the form of a single password.php file that you simply require to use), for those using PHP 5.3.7 and later and want to use this right now.

It only supports BCRYPT for now, but it aims to be easily extended to include other password hashing techniques and because the technique and cost is stored as part of the hash, changes to your prefered hashing technique/cost will not invalidate current hashes, the framework will automagically, use the correct technique/cost when validating. It also handles generating a "secure" salt if you do not explicitly define your own.

The API exposes four functions:

  • password_get_info() - returns information about the given hash
  • password_hash() - creates a password hash
  • password_needs_rehash() - checks if the given hash matches the given options. Useful to check if the hash conforms to your current technique/cost scheme allowing you to rehash if necessary
  • password_verify() - verifies that a password matches a hash

At the moment these functions accept the PASSWORD_BCRYPT and PASSWORD_DEFAULT password constants, which are synonymous at the moment, the difference being that PASSWORD_DEFAULT "may change in newer PHP releases when newer, stronger hashing algorithms are supported." Using PASSWORD_DEFAULT and password_needs_rehash() on login (and rehashing if necessary) should ensure that your hashes are reasonably resilient to brute-force attacks with little to no work for you.

EDIT: I just realised that this is mentioned briefly in Robert K's answer. I'll leave this answer here since I think it provides a bit more information about how it works and the ease of use it provides for those who don't know security.

  • 0
Reply Report

I just want to point out that PHP 5.5 includes a password hashing API that provides a wrapper around crypt(). This API significantly simplifies the task of hashing, verifying and rehashing password hashes. The author has also released a compatibility pack (in the form of a single password.php file that you simply require to use), for those using PHP 5.3.7 and later and want to use this right now.

It only supports BCRYPT for now, but it aims to be easily extended to include other password hashing techniques and because the technique and cost is stored as part of the hash, changes to your prefered hashing technique/cost will not invalidate current hashes, the framework will automagically, use the correct technique/cost when validating. It also handles generating a "secure" salt if you do not explicitly define your own.

The API exposes four functions:

  • password_get_info() - returns information about the given hash
  • password_hash() - creates a password hash
  • password_needs_rehash() - checks if the given hash matches the given options. Useful to check if the hash conforms to your current technique/cost scheme allowing you to rehash if necessary
  • password_verify() - verifies that a password matches a hash

At the moment these functions accept the PASSWORD_BCRYPT and PASSWORD_DEFAULT password constants, which are synonymous at the moment, the difference being that PASSWORD_DEFAULT "may change in newer PHP releases when newer, stronger hashing algorithms are supported." Using PASSWORD_DEFAULT and password_needs_rehash() on login (and rehashing if necessary) should ensure that your hashes are reasonably resilient to brute-force attacks with little to no work for you.

EDIT: I just realised that this is mentioned briefly in Robert K's answer. I'll leave this answer here since I think it provides a bit more information about how it works and the ease of use it provides for those who don't know security.

  • 0
Reply Report

I just want to point out that PHP 5.5 includes a password hashing API that provides a wrapper around crypt(). This API significantly simplifies the task of hashing, verifying and rehashing password hashes. The author has also released a compatibility pack (in the form of a single password.php file that you simply require to use), for those using PHP 5.3.7 and later and want to use this right now.

It only supports BCRYPT for now, but it aims to be easily extended to include other password hashing techniques and because the technique and cost is stored as part of the hash, changes to your prefered hashing technique/cost will not invalidate current hashes, the framework will automagically, use the correct technique/cost when validating. It also handles generating a "secure" salt if you do not explicitly define your own.

The API exposes four functions:

  • password_get_info() - returns information about the given hash
  • password_hash() - creates a password hash
  • password_needs_rehash() - checks if the given hash matches the given options. Useful to check if the hash conforms to your current technique/cost scheme allowing you to rehash if necessary
  • password_verify() - verifies that a password matches a hash

At the moment these functions accept the PASSWORD_BCRYPT and PASSWORD_DEFAULT password constants, which are synonymous at the moment, the difference being that PASSWORD_DEFAULT "may change in newer PHP releases when newer, stronger hashing algorithms are supported." Using PASSWORD_DEFAULT and password_needs_rehash() on login (and rehashing if necessary) should ensure that your hashes are reasonably resilient to brute-force attacks with little to no work for you.

EDIT: I just realised that this is mentioned briefly in Robert K's answer. I'll leave this answer here since I think it provides a bit more information about how it works and the ease of use it provides for those who don't know security.

  • 0
Reply Report

As of PHP 5.5, PHP has simple, secure functions for hashing and verifying passwords, password_hash() and password_verify()

$password = 'anna';
$hash = password_hash($password, PASSWORD_DEFAULT);
$expensiveHash = password_hash($password, PASSWORD_DEFAULT, array('cost' => 20));

password_verify('anna', $hash); //Returns true
password_verify('anna', $expensiveHash); //Also returns true
password_verify('elsa', $hash); //Returns false

When password_hash() is used, it generates a random salt and includes it in the outputted hash (along with the the cost and algorithm used.) password_verify() then reads that hash and determines the salt and encryption method used, and verifies it against the provided plaintext password.

Providing the PASSWORD_DEFAULT instructs PHP to use the default hashing algorithm of the installed version of PHP. Exactly which algorithm that means is intended to change over time in future versions, so that it will always be one of the strongest available algorithms.

Increasing cost (which defaults to 10) makes the hash harder to brute-force but also means generating hashes and verifying passwords against them will be more work for your server's CPU.

Note that even though the default hashing algorithm may change, old hashes will continue to verify just fine because the algorithm used is stored in the hash and password_verify() picks up on it.

  • 2
Reply Report

A much shorter and safer answer - don't write your own password mechanism at all, use a tried and tested mechanism.

  • PHP 5.5 or higher: password_hash() is good quality and part of PHP core.
  • Older PHP versions: OpenWall's phpass library is much better than most custom code - used in WordPress, Drupal, etc.

Most programmers just don't have the expertise to write crypto related code safely without introducing vulnerabilities.

Quick self-test: what is password stretching and how many iterations should you use? If you don't know the answer, you should use password_hash(), as password stretching is now a critical feature of password mechanisms due to much faster CPUs and the use of GPUs and FPGAs to crack passwords at rates of billions of guesses per second (with GPUs).

For example, you can crack all 8-character Windows passwords in 6 hours using 25 GPUs installed in 5 desktop PCs. This is brute-forcing i.e. enumerating and checking every 8-character Windows password, including special characters, and is not a dictionary attack. That was in 2012, as of 2018 you could use fewer GPUs, or crack faster with 25 GPUs.

There are also many rainbow table attacks on Windows passwords that run on ordinary CPUs and are very fast. All this is because Windows still doesn't salt or stretch its passwords, even in Windows 10 - don't make the same mistake as Microsoft did!

See also:

  • excellent answer with more about why password_hash() or phpass are the best way to go.
  • good blog article giving recommmended 'work factors' (number of iterations) for main algorithms including bcrypt, scrypt and PBKDF2.
  • 1
Reply Report