Search code examples
phppasswordsbase64salt-cryptographycrypt

Do I need base64 encode my salt (for hashing passwords)?


Excuse me for this very odd question. I understand the purpose of base64 encoding for transmitting data (i.e. MIME's Base64 encoding), but I don't know if I need to base64 encode my salts.

I wrote an utility class (a base abstract class indeed):

use Symfony\Component\Security\Core\Encoder\BasePasswordEncoder;

abstract class AbstractCryptPasswordEncoder extends BasePasswordEncoder
{
    /**
     * @return string
     */
    protected abstract function getSaltPrefix();

    /**
     * @return string
     */
    protected abstract function getSalt();

    /**
     * {@inheritdoc}
     */
    public function encodePassword($raw, $salt = null)
    {
        return crypt($raw, $this->getSaltPrefix().$this->getSalt());
    }

    /**
     * {@inheritdoc}
     */
    public function isPasswordValid($encoded, $raw, $salt = null)
    {
        return $encoded === crypt($raw, $encoded);
    }
}

A real implementation class would be:

class Sha512CryptPasswordEncoder extends AbstractCryptPasswordEncoder
{
    /**
     * @var string
     */
    private $rounds;

    /**
     * @param null|int $rounds The number of hashing loops
     */
    public function __construct($rounds = null)
    {
        $this->rounds = $rounds;
    }

    /**
     * {@inheritdoc}
     */
    protected  function getSaltPrefix()
    {
        return sprintf('$6$%s', $this->rounds ? "rounds={$this->rounds}$" : '');
    }

    /**
     * {@inheritdoc}
     */
    protected function getSalt()
    {
        return base64_encode(openssl_random_pseudo_bytes(12));
    }
}

The key part is the salt generation, which will be embedded in the password: do I need base64_encode for any reason (storing), assuming that it will be never sent over the wire?


Solution

  • Each hash algorithm expects a salt in a given alphabet, that means using base64_encode() can be the right thing, but often it does either not use the full alphabet or returns characters that are not in this alphabet.

    Take BCrypt for example, this is a good hash algorithm for passwords (SHA-512 is not appropriate because it is too fast), it accepts all characters of a base64-encoded string except the '+' character. On the other side it accepts '.' characters that are not part of a base64-encoded string.

    PHP 5.5 will have the functions password_hash() and password_verify() ready, to make the usage of BCrypt easier, i really can recommend them. There is also a compatibility pack available for older PHP versions, on line 121 you can see that base64_encode() is indeed used, but afterwards all invalid '+' characters are replaced with allowed '.' characters:

    Encoding a salt for BCrypt:

    $salt = str_replace('+', '.', base64_encode($buffer));