I'm trying to build a simple password generating function using openssl_random_pseudo_bytes and want to know if it is cryptographically strong.
function randomPassword($length = 12) {
mt_srand( hexdec( bin2hex( openssl_random_pseudo_bytes(256) ) ) );
$alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!##$&*-_=+.?';
$pass = '';
$alphaLength = strlen($alphabet) - 1;
for ($i = 0; $i < $length; $i++) {
$n = mt_rand(0, $alphaLength);
$pass .= $alphabet[$n];
}
return $pass;
}
I would assume that if there is a weakness that is is till in the mt_rand function regardless of seeding it with openssl_random_pseudo_bytes. However, I have been unable to find any discussion on the topic.
According to the documentation
openssl_random_pseudo_bytes is not guaranteed to produce cryptographically strong random data. However, in the improbable case that it was not able to use a cryptographically strong RNG, it will at least tell you. That's what openssl_random_pseudo_bytes's second parameter is for:
It also indicates if a cryptographically strong algorithm was used to produce the pseudo-random bytes, and does this via the optional crypto_strong parameter. It's rare for this to be FALSE, but some systems may be broken or old.
You can pass this parameter and then check its value:
$bytes = openssl_random_pseudo_bytes(256, $strong);
if (!$strong) {
// handle error as necessary.
}
However, opposed to openssl_random_pseudo_bytes, mt_rand is explicitly stated to not produce cryptographically secure values.
This function does not generate cryptographically secure values, and should not be used for cryptographic purposes.
I'm no expert, but I doubt that seeding a Mersenne Twister with secure random data would make the generated data any more secure (if at all, probably just the first byte):
Counter-suggestion
You did not state which version of PHP you're using. If you are using PHP 7, random_int might make a good alternative for you (it's also explicitly stated to be cryptographically secure):
for ($i = 0; $i < $length; $i++) {
$n = random_int(0, $alphaLength);
$pass .= $alphabet[$n];
}
Alternatively, this answer provides a secure, PHP-based implementation for a rand function that uses OpenSSL as an CSPRNG that you could use in place of random_int.
Related
Here's the function I'm using to generate random salts:
function generateRandomString($nbLetters){
$randString="";
$charUniverse="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for($i=0; $i<$nbLetters; $i++){
$randInt=rand(0,61);
$randChar=$charUniverse[$randInt];
$randString=$randomString.$randChar;
}
return $randomString;
}
This is for a non commercial website. It's only used to generate the salt (to be stored in the db and used along with the user submitted pw for hashing).
Is this appropriate? Should I use a larger subset of characters, and if so is there an easy way to do that in PHP?
If you are hashing passwords, you should use a modern hashing algorithm that does not require you to generate your own salt. Using weak hashing algorithms presents a danger to both you and your users. My original answer was written eight years ago. Times have changed, and password hashing is a lot easier now.
You should always use built in functions to hash/check passwords. Using your own algorithms at any point introduces a huge amount of unnecessary risk.
For PHP, consider using password_hash(), with the PASSWORD_BCRYPT algorithm. There is no need to provide your own salt.
Below is my original answer, for posterity:
Warning: The following implementation does not produce an unpredictable salt, as per the documentation for uniqid.
From the php sha1 page:
$salt = uniqid(mt_rand(), true);
This looks simpler, and more effective (since each is unique) than what you have proposed.
If you're on Linux, /dev/urandom is probably your best source of randomness. It's supplied by the OS itself, so it's guaranteed to be much more reliable than any PHP built-in function.
$fp = fopen('/dev/urandom', 'r');
$randomString = fread($fp, 32);
fclose($fp);
This will give you 32 bytes of random blob. You'll probably want to pass this through something like base64_encode() to make it legible. No need to juggle characters yourself.
Edit 2014: In PHP 5.3 and above, openssl_random_pseudo_bytes() is the easiest way to get a bunch of random bytes. On *nix systems, it uses /dev/urandom behind the scenes. On Windows systems, it uses a different algorithm that is built into the OpenSSL library.
Related: https://security.stackexchange.com/questions/26206
Related: should i use urandom or openssl_random_pseudo_bytes?
password_hash() is availble in PHP 5.5 and newer. I am surprised to learn it is not mentioned here.
With password_hash() there is no need to generate a salt as the salt is automatically being generated using the bcrypt algorithm -- and therefore no need to make up a set of characters.
Instead, the user-submitted password is compared to the unique password hash stored in the database using password_verify(). Just store Username and Password hash in the user database table, you will then be able to compare it to a user-submitted password using password_verify().
How password hash()'ing works:
The password_hash() function outputs a unique password hash, when storing the string in a database -- it is recommended that the column allows up to 255 characters.
$password = "goat";
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
// Output example (store this in the database)
$2y$10$GBIQaf6gEeU9im8RTKhIgOZ5q5haDA.A5GzocSr5CR.sU8OUsCUwq <- This hash changes.
$2y$10$7.y.lLyEHKfpxTRnT4HmweDKWojTLo1Ra0hXXlAC4ra1pfneAbj0K
$2y$10$5m8sFNEpJLBfMt/3A0BI5uH4CKep2hiNI1/BnDIG0PpLXpQzIHG8y
To verify a hashed password, you use password_verify():
$password_enc = password_hash("goat", PASSWORD_DEFAULT);
dump(password_verify('goat', $password_enc)); // TRUE
dump(password_verify('fish', $password_enc)); // FALSE
If you prefer, salt can be added manually as an option, like so:
$password = 'MyPassword';
$salt = 'MySaltThatUsesALongAndImpossibleToRememberSentence+NumbersSuch#7913';
$hash = password_hash($password, PASSWORD_DEFAULT, ['salt'=>$salt]);
// Output: $2y$10$TXlTYWx0VGhhdFVzZXNBT.ApoIjIiwyhEvKC9Ok5qzVcSal7T8CTu <- This password hash not change.
Replace rand(0,61) with mt_rand(0, 61) and you should be fine (Since mt_rand is better at producing random numbers)...
But more important than strength of the salt is the way you hash it. If you have a great salt routine, but only do md5($pass.$salt), you're throwing away the salt. I personally recommend stretching the hash... For example:
function getSaltedHash($password, $salt) {
$hash = $password . $salt;
for ($i = 0; $i < 50; $i++) {
$hash = hash('sha512', $password . $hash . $salt);
}
return $hash;
}
For more information on hash stretching, check out this SO answer...
I would take advice from another answer and use mt_rand(0, 61), because the Mersenne Twister produces better entropy.
Additionally, your function is really two parts: generating random $nbLetters digits and encoding that in base62. This will make things much clearer to a maintenance programmer (maybe you!) who stumbles across it a few years down the road:
// In a class somewhere
private $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private function getBase62Char($num) {
return $chars[$num];
}
public function generateRandomString($nbLetters){
$randString="";
for($i=0; $i < $nbLetters; $i++){
$randChar = getBase62Char(mt_rand(0,61));
$randString .= $randChar;
}
return $randomString;
}
This is my method, It uses truly random numbers from atmospheric noise. It is all mixed in with pseudo-random values and strings. Shuffled and hashed. Here is my code: I call it overkill.
<?php
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return $randomString;
}
function get_true_random_number($min = 1, $max = 100) {
$max = ((int) $max >= 1) ? (int) $max : 100;
$min = ((int) $min < $max) ? (int) $min : 1;
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_ENCODING => '',
CURLOPT_USERAGENT => 'PHP',
CURLOPT_AUTOREFERER => true,
CURLOPT_CONNECTTIMEOUT => 120,
CURLOPT_TIMEOUT => 120,
CURLOPT_MAXREDIRS => 10,
);
$ch = curl_init('http://www.random.org/integers/?num=1&min='
. $min . '&max=' . $max . '&col=1&base=10&format=plain&rnd=new');
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
curl_close($ch);
if(is_numeric($content)) {
return trim($content);
} else {
return rand(-10,127);
}
}
function generateSalt() {
$string = generateRandomString(10);
$int = get_true_random_number(-2,123);
$shuffled_mixture = str_shuffle(Time().$int.$string);
return $salt = md5($shuffled_mixture);
}
echo generateSalt();
?>
The atmospheric noise is provided by random.org. I have also seen truly random generation from images of lava lamps that are interpreted via hue and location. (Hue is location)
Here is a much better way if you have windows and cant do /dev/random.
//Key generator
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
//The variable $secure is given by openssl_random_ps... and it will give a true or false if its tru then it means that the salt is secure for cryptologic.
while(!$secure){
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
}
I think that a very good salt for example is the user name (if you are talking about pw hashing and the user name doesn't change.)
You don't need to generate anything and don't need to store further data.
A fairly simple technique:
$a = array('a', 'b', ...., 'A', 'B', ..., '9');
shuffle($a);
$salt = substr(implode($a), 0, 2); // or whatever sized salt is wanted
Unlike uniqid() it generates a random result.
I use this:
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
If you want ultimate unique salt you should use a unique value entered and required by the user such as the email or the username, then hashing it using sha1 and then merge it - concatenate - with the salt value generated by your code.
Another, you have to extend $charUniverse by the mean of some special characters such as #,!#- etc.
I am using the below code to generate random string which I then insert to the database, and I would like to know if it is collision free? Is there a better way that is collision free?
function generate_random_name()
{
$length = 12;
$charactersa = "mRdxIU2r3dxa1B2c3D4e5F6g7H8i9Jqk1L2m13N1oF5P6qW7R1Ls9T20u21V2w3X4y2Z2x6";
$real_string_legntha = strlen($charactersa) - 1;
$stringa=rand(12345,09876);
for ($oluvica = 0; $oluvica < $length; $oluvica++)
{
$stringa .= $charactersa[mt_rand(0, $real_string_legntha)];
}
return $stringa;
}
$unique = generate_random_name();
Make use of uniqid() [With more entropy flag enabled] coupled with time()
<?php
echo uniqid(time(), true);
This will be more than enough. Coupling hashes like sha-256 and sha-512 will generate more unique outputs.
Some more info
A while back I wrote a random string generator that builds a string using the mt_rand()th character in a string until the desired length is reached.
public function getPassword ()
{
if ($this -> password == '')
{
$pw = '';
$charListEnd = strlen (static::CHARLIST) - 1;
for ($loops = mt_rand ($this -> min, $this -> max); $loops > 0; $loops--)
{
$pw .= substr (static::CHARLIST, mt_rand (0, $charListEnd), 1);
}
$this -> password = $pw;
}
return $this -> password;
}
(CHARLIST is a class constant containing a pool of characters for the password. $min and $max are length contraints)
Today, when researching something else entirely I stumbled upon the following code:
function generateRandomString ($length = 10) {
return substr(str_shuffle ("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length);
}
This accomplishes pretty much the same effect as my looping mt_rand() based code in one line. I really like it for that simple reason, fewer lines of code is always a good thing. :)
But when I looked up str_shuffle in PHP's manual the documentation on it was pretty light. One thing I was really keen to learn was what algorithm does it use for randomness? The manual doesn't mention what kind of randomization is done to get the shuffled string. If it uses rand() instead of mt_rand() then sticking to my current solution may be better after all.
So basically I'd like to know how str_shuffle randomizes the string. Is it using rand() or mt_rand()? I'm using my random string function to generate passwords, so the quality of the randomness matters.
UPDATE: As has been pointed out, the str_shuffle method is not equivalent to the code I'm already using and will be less random due to the string's characters remaining the same as the input, only with their order changed. However I'm still curious as to how the str_shuffle function randomizes its input string.
A better solution would be mt_rand which uses Mersenne Twister which much more better.
As has been pointed out, the str_shuffle method is not equivalent to the code I'm already using and will be less random due to the string's characters remaining the same as the input, only with their order changed. However I'm still curious as to how the str_shuffle function randomizes its input string.
To make the output equal lets just use 0,1 and look at the visual representation of each of the functions
Simple Test Code
header("Content-type: image/png");
$im = imagecreatetruecolor(512, 512) or die("Cannot Initialize new GD image stream");
$white = imagecolorallocate($im, 255, 255, 255);
for($y = 0; $y < 512; $y ++) {
for($x = 0; $x < 512; $x ++) {
if (testMTRand()) { //change each function here
imagesetpixel($im, $x, $y, $white);
}
}
}
imagepng($im);
imagedestroy($im);
function testMTRand() {
return mt_rand(0, 1);
}
function testRand() {
return rand(0, 1);
}
function testShuffle() {
return substr(str_shuffle("01"), 0, 1);
}
Output testRand()
Output testShuffle()
Output testMTRand()
So basically I'd like to know how str_shuffle randomizes the string. Is it using rand() or mt_rand()? I'm using my random string function to generate passwords, so the quality of the randomness matters.
You can see clearly that str_shuffle produces almost same output as rand ...
Please be aware that this method should not be used if your application is really focused on security. The Mersenne Twister is NOT cryptographically secure. A PRNG can yield values which statistically appear to be random but still are easy to break.
Still not cryptographically secure, but here is a way to use str_shuffle() while allowing character repetition, thereby improving complexity...
generate_password($length = 8, $strength = 3) {
if ($length < 6) $length = 6;
if ($length > 32) $length = 32;
// Excludes [0,O,o,1,I,i,L,l,1] on purpose for readability
$chars = 'abcdefghjkmnpqrstuvwxyz';
if ($strength >= 2) $chars .= '23456789';
if ($strength >= 3) $chars .= strtoupper($lower);
if ($strength >= 4) $chars .= '!##$%&?';
return substr(str_shuffle(str_repeat($chars, $length)), 0, $length);
}
$chars is repeated $length times before the string is shuffled to make this a little better than shuffling only single occurrence.
We only use this in systems that do not store sensitive information ;)
I found this openssl_random_pseudo_bytes functions from php.net.
function generate_password($length = 24) {
if(function_exists('openssl_random_pseudo_bytes')) {
$password = base64_encode(openssl_random_pseudo_bytes($length, $strong));
if($strong == TRUE)
return substr($password, 0, $length); //base64 is about 33% longer, so we need to truncate the result
}
# fallback to mt_rand if php < 5.3 or no openssl available
$characters = '0123456789';
$characters .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/+';
$charactersLength = strlen($characters)-1;
$password = '';
# select some random characters
for ($i = 0; $i < $length; $i++) {
$password .= $characters[mt_rand(0, $charactersLength)];
}
return $password;
}
But I want to be sure that whether the value it generates is repeatable or not? I am looking for a result which is not repeatable.
I have been told that the value from mt_rand() is not repeatable? but a random number should be repeatable as it self-explained already - mt_rand, doesn't it?
EDIT:
for instance, I just tested it and it has generated W8hhkS+ngIl7DxxFDxEx6gSn. but if will generate the same value again in the future - then it is repeatable.
random_pseudo_bytes is not repeatable. It is true that any output of finite length must eventually repeat, for example if you specify a length of 1 byte, then there is only 256 possible strings, so you must get the same string you had before no later than after 256 attempts (and likely quite a bit sooner).
But you're talking practically and not mathemathically, and have a default length of 24.
Yes, picking random 24-byte strings will eventually give you a string that you had before, but that's only true in the mathemathical universe. In the real physical universe, there's 6277101735386680763835789423207666416102355444464034512896 possible such strings, which means that even if you generated billions of passwords this way every second, you'd still not be likely to get the same string twice in a million years.
Any random function, including the one above, is repeatable.
Perhaps you're looking for the PHP function uniqid?
Here's the function I'm using to generate random salts:
function generateRandomString($nbLetters){
$randString="";
$charUniverse="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
for($i=0; $i<$nbLetters; $i++){
$randInt=rand(0,61);
$randChar=$charUniverse[$randInt];
$randString=$randomString.$randChar;
}
return $randomString;
}
This is for a non commercial website. It's only used to generate the salt (to be stored in the db and used along with the user submitted pw for hashing).
Is this appropriate? Should I use a larger subset of characters, and if so is there an easy way to do that in PHP?
If you are hashing passwords, you should use a modern hashing algorithm that does not require you to generate your own salt. Using weak hashing algorithms presents a danger to both you and your users. My original answer was written eight years ago. Times have changed, and password hashing is a lot easier now.
You should always use built in functions to hash/check passwords. Using your own algorithms at any point introduces a huge amount of unnecessary risk.
For PHP, consider using password_hash(), with the PASSWORD_BCRYPT algorithm. There is no need to provide your own salt.
Below is my original answer, for posterity:
Warning: The following implementation does not produce an unpredictable salt, as per the documentation for uniqid.
From the php sha1 page:
$salt = uniqid(mt_rand(), true);
This looks simpler, and more effective (since each is unique) than what you have proposed.
If you're on Linux, /dev/urandom is probably your best source of randomness. It's supplied by the OS itself, so it's guaranteed to be much more reliable than any PHP built-in function.
$fp = fopen('/dev/urandom', 'r');
$randomString = fread($fp, 32);
fclose($fp);
This will give you 32 bytes of random blob. You'll probably want to pass this through something like base64_encode() to make it legible. No need to juggle characters yourself.
Edit 2014: In PHP 5.3 and above, openssl_random_pseudo_bytes() is the easiest way to get a bunch of random bytes. On *nix systems, it uses /dev/urandom behind the scenes. On Windows systems, it uses a different algorithm that is built into the OpenSSL library.
Related: https://security.stackexchange.com/questions/26206
Related: should i use urandom or openssl_random_pseudo_bytes?
password_hash() is availble in PHP 5.5 and newer. I am surprised to learn it is not mentioned here.
With password_hash() there is no need to generate a salt as the salt is automatically being generated using the bcrypt algorithm -- and therefore no need to make up a set of characters.
Instead, the user-submitted password is compared to the unique password hash stored in the database using password_verify(). Just store Username and Password hash in the user database table, you will then be able to compare it to a user-submitted password using password_verify().
How password hash()'ing works:
The password_hash() function outputs a unique password hash, when storing the string in a database -- it is recommended that the column allows up to 255 characters.
$password = "goat";
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
echo password_hash($password, PASSWORD_DEFAULT);
// Output example (store this in the database)
$2y$10$GBIQaf6gEeU9im8RTKhIgOZ5q5haDA.A5GzocSr5CR.sU8OUsCUwq <- This hash changes.
$2y$10$7.y.lLyEHKfpxTRnT4HmweDKWojTLo1Ra0hXXlAC4ra1pfneAbj0K
$2y$10$5m8sFNEpJLBfMt/3A0BI5uH4CKep2hiNI1/BnDIG0PpLXpQzIHG8y
To verify a hashed password, you use password_verify():
$password_enc = password_hash("goat", PASSWORD_DEFAULT);
dump(password_verify('goat', $password_enc)); // TRUE
dump(password_verify('fish', $password_enc)); // FALSE
If you prefer, salt can be added manually as an option, like so:
$password = 'MyPassword';
$salt = 'MySaltThatUsesALongAndImpossibleToRememberSentence+NumbersSuch#7913';
$hash = password_hash($password, PASSWORD_DEFAULT, ['salt'=>$salt]);
// Output: $2y$10$TXlTYWx0VGhhdFVzZXNBT.ApoIjIiwyhEvKC9Ok5qzVcSal7T8CTu <- This password hash not change.
Replace rand(0,61) with mt_rand(0, 61) and you should be fine (Since mt_rand is better at producing random numbers)...
But more important than strength of the salt is the way you hash it. If you have a great salt routine, but only do md5($pass.$salt), you're throwing away the salt. I personally recommend stretching the hash... For example:
function getSaltedHash($password, $salt) {
$hash = $password . $salt;
for ($i = 0; $i < 50; $i++) {
$hash = hash('sha512', $password . $hash . $salt);
}
return $hash;
}
For more information on hash stretching, check out this SO answer...
I would take advice from another answer and use mt_rand(0, 61), because the Mersenne Twister produces better entropy.
Additionally, your function is really two parts: generating random $nbLetters digits and encoding that in base62. This will make things much clearer to a maintenance programmer (maybe you!) who stumbles across it a few years down the road:
// In a class somewhere
private $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private function getBase62Char($num) {
return $chars[$num];
}
public function generateRandomString($nbLetters){
$randString="";
for($i=0; $i < $nbLetters; $i++){
$randChar = getBase62Char(mt_rand(0,61));
$randString .= $randChar;
}
return $randomString;
}
This is my method, It uses truly random numbers from atmospheric noise. It is all mixed in with pseudo-random values and strings. Shuffled and hashed. Here is my code: I call it overkill.
<?php
function generateRandomString($length = 10) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, strlen($characters) - 1)];
}
return $randomString;
}
function get_true_random_number($min = 1, $max = 100) {
$max = ((int) $max >= 1) ? (int) $max : 100;
$min = ((int) $min < $max) ? (int) $min : 1;
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_ENCODING => '',
CURLOPT_USERAGENT => 'PHP',
CURLOPT_AUTOREFERER => true,
CURLOPT_CONNECTTIMEOUT => 120,
CURLOPT_TIMEOUT => 120,
CURLOPT_MAXREDIRS => 10,
);
$ch = curl_init('http://www.random.org/integers/?num=1&min='
. $min . '&max=' . $max . '&col=1&base=10&format=plain&rnd=new');
curl_setopt_array($ch, $options);
$content = curl_exec($ch);
curl_close($ch);
if(is_numeric($content)) {
return trim($content);
} else {
return rand(-10,127);
}
}
function generateSalt() {
$string = generateRandomString(10);
$int = get_true_random_number(-2,123);
$shuffled_mixture = str_shuffle(Time().$int.$string);
return $salt = md5($shuffled_mixture);
}
echo generateSalt();
?>
The atmospheric noise is provided by random.org. I have also seen truly random generation from images of lava lamps that are interpreted via hue and location. (Hue is location)
Here is a much better way if you have windows and cant do /dev/random.
//Key generator
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
//The variable $secure is given by openssl_random_ps... and it will give a true or false if its tru then it means that the salt is secure for cryptologic.
while(!$secure){
$salt = base64_encode(openssl_random_pseudo_bytes(128, $secure));
}
I think that a very good salt for example is the user name (if you are talking about pw hashing and the user name doesn't change.)
You don't need to generate anything and don't need to store further data.
A fairly simple technique:
$a = array('a', 'b', ...., 'A', 'B', ..., '9');
shuffle($a);
$salt = substr(implode($a), 0, 2); // or whatever sized salt is wanted
Unlike uniqid() it generates a random result.
I use this:
$salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTES, MCRYPT_DEV_URANDOM));
If you want ultimate unique salt you should use a unique value entered and required by the user such as the email or the username, then hashing it using sha1 and then merge it - concatenate - with the salt value generated by your code.
Another, you have to extend $charUniverse by the mean of some special characters such as #,!#- etc.