What is the best way to create a random hash/string? - php

What is the best way of generating a hash for the purpose of storing a session? I am looking for a lightweight, portable solution.

bin2hex(mcrypt_create_iv(22, MCRYPT_DEV_URANDOM));
mcrypt_create_iv will give you a random sequence of bytes.
bin2hex will convert it to ASCII text
Example output:
d2c63a605ae27c13e43e26fe2c97a36c4556846dd3ef
Bare in mind that "best" is a relative term. You have a tradeoff to make between security, uniqueness and speed. The above example is good for 99% of the cases, though if you are dealing with a particularly sensitive data, you might want to read about the difference between MCRYPT_DEV_URANDOM and MCRYPT_DEV_RANDOM.
Finally, there is a RandomLib "for generating random numbers and strings of various strengths".
Notice that so far I have assumed that you are looking to generate a random string, which is not the same as deriving a hash from a value. For the latter, refer to password_hash.

random_bytes() is available as of PHP 7.0 (or use this polyfill for 5.2 through 5.6). It is cryptographically secure (compared to rand() which is not) and can be used in conjunction with bin2hex(), base64_encode(), or any other function that converts binary to a string that's safe for your use case.
As a hexadecimal string
bin2hex() will result in a hexadecimal string that's twice as many characters as the number of random bytes (each hex character represents 4 bits while there are 8 bits in a byte). It will only include characters from abcdef0123456789 and the length will always be an increment of 2 (regex: /^([a-f0-9]{2})*$/).
$random_hex = bin2hex(random_bytes(18));
echo serialize($random_hex);
s:36:"ee438d1d108bd818aa0d525602340e5d7036";
As a base64 string
base64_encode() will result in a string that's about 33% longer than the number of random bytes (each base64 character represents 6 bits while there are 8 bits in a byte). It's length will always be an increment of 4, with = used to pad the end of the string and characters from the following list used to encode the data (excluding whitespace that I added for readability):
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
/+
To take full advantage of the space available, it's best to provide an increment of 3 to random_bytes(). The resulting string will match /^([a-zA-Z\/+=]{4})*$/, although = can only appear at the end as = or == and only when a number that is not an increment of 3 is provided to random_bytes().
$random_base64 = base64_encode(random_bytes(18));
echo serialize($random_base64);
s:24:"ttYDDiGPV5K0MXbcfeqAGniH";

You can use PHP's built-in hashing functions, sha1 and md5. Choose one, not both.
One may think that using both, sha1(md5($pass)) would be a solution. Using both does not make your password more secure, its causes redundant data and does not make much sense.
Take a look at PHP Security Consortium: Password Hashing they give a good article with weaknesses and improving security with hashing.
Nonce stands for "numbers used once". They are used on requests to prevent unauthorized access, they send a secret key and check the key each time your code is used.
You can check out more at PHP NONCE Library from FullThrottle Development

Maybe uniqid() is what you need?
uniqid — Generate a unique ID

You can use openssl_random_pseudo_bytes since php 5.3.0 to generate a pseudo random string of bytes. You can use this function and convert it in some way to string using one of these methods:
$bytes = openssl_random_pseudo_bytes(32);
$hash = base64_encode($bytes);
or
$bytes = openssl_random_pseudo_bytes(32);
$hash = bin2hex($bytes);
The first one will generate the shortest string, with numbers, lowercase, uppercase and some special characters (=, +, /). The second alternative will generate hexadecimal numbers (0-9, a-f)

Use random_bytes() if it's available!
$length = 32;
if (function_exists("random_bytes")) {
$bytes = random_bytes(ceil($length / 2));
$token = substr(bin2hex($bytes), 0, $length)
}
Check it on php.net

I personally use apache's mod_unique_id to generate a random unique number to store my sessions. It's really easy to use (if you use apache).
For nonce take a look here http://en.wikipedia.org/wiki/Cryptographic_nonce there's even a link to a PHP library.

I generally dont manually manage session ids. Ive seen something along these lines recommended for mixing things up a bit before, ive never used myself so i cant attest to it being any better or worse than the default (Note this is for use with autogen not with manual management).
//md5 "emulation" using sha1
ini_set('session.hash_function', 1);
ini_set('session.hash_bits_per_character', 5);

Different people will have different best ways. But this is my way:
Download this rand-hash.php file :
http://bit.ly/random-string-generator
include() it in the php script that you are working with. Then, simply call
cc_rand() function. By default it will return a 6 characters long
random string that may include a-z, A-Z, and 0-9. You can pass
length to specify how many characters cc_rand() should return.
Example:
cc_rand() will return something like: 4M8iro
cc_rand(15) will return something similar to this: S4cDK0L34hRIqAS
Cheers!

Related

Scratch generate by openssl_random_pseudo_bytes PHP

I need to create a function to generate a string for scratching.
I was recommended to use openssl_random_pseudo_bytes() function on php. But it generate a result which contains some special ascii character like ����P. When I use bin2hex it's ok but it doesn't contains other character like w,q...
Is there some other methods?
Unfortunately, bin2hex() gives you a hexadecimal representation of the binary values. Hexadecimal can only represent 0-9 and A-F since it's a base 16 number system. However, you could do something like the first answer to this question: PHP random string generator to generate random strings with Qs and Ws.

Most space efficient encoding that generate printable string in PHP?

I have a large string $string that when applied to md5(), give me
c4ca4238a0b923820dcc509a6f75849b
The length is 32, I want to reduce it, so
base64_encode(md5($string, true));
xMpCOKC5I4INzFCab3WEmw==
Removing the last two == it give me a string with length = 22.
Are there any other better algorithms?
I am not sure you realised that md5 is a hash function, and therefore irreversible. If you do not care about reversibility, you could just as well trim the md5 hash (or any hash of your liking*) down to an arbitrary number of characters. All this would do is increase the likelihood of collision (I feel this does not produce an uniform distribution though).
If you are looking for a reversible (ie. non-destructive) compression, then do not reinvent the wheel. Use the built-in functions, such as gzdeflate() or gzcompress(), or other similar functions.
*Here is a list of hash functions (wikipedia) along with the size of their output.
I suppose the smallest possible "hash function" would be a parity bit :)
One better way would be to, instead of converting to binary to hexadecimal (as md5 does) and then converting the string to base64, instead convert from the hexadecimal md5 directly to base64.
Since hexadecimal is 16 bits per character, and base64 is 64 bits per character, every 2 hexadecimal characters will make up one base64 character.
To perform the conversion, you can do the following:
Split the string into sixteen 2 character chunks
The first character should be multiplied by 2 and added to the second (keeping in mind that A-F = 10-15).
This number can be matched to the base64 scheme using the table from here: https://en.wikipedia.org/wiki/Base64
This will result in a 16 character base64 string with the same value as the hexadecimal representation of the md5 string.
Theoretically, you could do the same for any base. If we had a way to encode base128 strings in ASCII, we could end up with an 8 character string. However, as the character set is limited, I think base64 is the highest base that is commonly used.
The smaller the length of the string you want .. the smaller the number of possible combination
Total Number of Possibility with reputation
Total Possibility = nr
Since we are dealing with base64 has the printable output this means we only have 64 characters
n = 64
If you are looking at 22 letters in length
nr = 6422 = 5,444,517,870,735,015,415,413,993,718,908,291,383,296 possibilities
Back to your question : Are there any better algorithm?
Truncate the string with a good hash to desired length you want since the total possibility and collision is fixed
$string = "the fox jumps over the lazy brown dog";
echo truncateHash($string, 8);
Output
9TWbFjOl
Function Used
function truncateHash($str, $length) {
$hash = hash("sha256", $str, true);
return substr(base64_encode($hash), 0, $length);
}
This encoding generates shorter string,
print base64_encode(hash("crc32b",$string,1));
output
qfQIdw==
Not sure if MD5 is the right choice for you, but i will assume that you have reason to stick with this algorithm and are looking for a shorter representation. There are several possibilities to generate a shorter string with different alphabets:
Option 1: Binary string
The shortest possbile form of an MD5 is it's binary representation, to get such a string you can simply call:
$binaryMd5 = md5($input, true);
This string you can store like any other string in a database, it needs only 16 characters. Just make sure you do a proper escaping, either with mysqli_real_escape_string() or with parametrized queries (PDO).
Option 2: Base64 encoding
Base64 encoding will produce a string with this alphabet: [0-9 A-Z a-z + /] and uses '=' as padding. This encoding is very fast, but includes the sometimes unwanted characters '+/='.
$base64Md5 = base64_encode(md5($input, true));
The output length will be always 24 characters for the MD5 hash.
Option 3: Base62 encoding
The base62 encoding only uses the alphabet [0-9 A-Z a-z]. Such strings can be safely used for any purpose like tokens in an URL, and they are very compact. I wrote a base62 encoder, which is able to convert binary strings to the base62 alphabet. It may not be the fastest possible implementation, but it was my goal to write understandable code. The same class could be easily adapted to different alphabets.
$base62Md5 = StoBase62Encoder::base62encode(md5($input, true));
The output length will vary from 16 to 22 characters for the MD5 hash.
Base 91 looks like the most space efficient binary to ASCII printable encoding algorithm (which is what it seems you want).
I've not seen the PHP implementation, but if your software has to work with others I'd stick to Base 64; it's well-known, lightning fast, and available everywhere.
Firstly, to answer your question: Yes, there is a better algorithm (if with "better" you mean "shorter").
Use the hash() function (which has been part of the PHP core and enabled by default since PHP 5.1.2.) with any of the adler32, fnv132, crc32, crc32b, fnv132 or joaat algorithms.
Without a more in-depth knowledge of your current situation, you might as well just pick whichever one you think sounds the coolest.
Here is an example:
hash('crc32b', $string)
I set up an online example you can play around with.
Secondly, I would like to point out that what you are asking is an almost exact duplicate of another question here on stackoverflow.
I read from your post that you are searching for a hashing algorithm and not compression.
There are various standard hashing algorithms in php out there. Have a look at PHP hashing functions.
Depending on what you want to hash there are different approches. Be careful and calculate the average collision probability.
However it seems you are searching for a 'compression' which outputs the minimum possible size of chars for a given string. If you do, then have a look at Lempel–Ziv–Welch (php implementation) or others.

PHP char type encryption

While finishing my websites java login program and page, I decided to pass an encypted value through the URL to a validation page as an extra line of security. I have an encyrption algorithim that I wrote long ago that no one I know has cracked yet so I want to use that. But I need chars for it to properly work. From what I can tell, PHP doesn't have a char type. So my question is first, is their a char type, and secondly, is it possible to convert that to an int? Side Note: Login is a signed applet so all pages are in PHP. Edit: Forgot to mention that this is just the base of encryption and I will be adding to the algorithim.
You can reference a character in a string $str by $str[$index].
The ord function will return a character's integer value:
$val = ord($str[$index]);
The chr function does the opposite:
$char = chr($val);//$char == $str[$index]
You can access a string $s character by character by referring to $s[$i]. ord($s) gets the ASCII value of a character, chr($n) gets the character corresponding to an ASCII value.
Don't use your own cryptographic primitives unless you know what you are doing! Use PHP's own implementations of known strong algorithms (e.g. AES-256). Just because no one you know has cracked your custom algorithm doesn't mean someone else can't.
There is no char type in PHP, and the string type does not readily convert to int. PHP handles dynamic type-juggling, so type declarations are not used.
On a side note, "no one has cracked [my encryption algorithm] yet" doesn't necessarily mean someone won't in the future. If you're encrypting important stuff, use the standard encryption algorithms - they're standard for a reason.

PHP crypt function password encoding

The following code returns the same encrypted password whichever way round. Why and how do we stop this.
$pwd = 'shits8888';
$salt = '50153fc193af9';
echo crypt($pwd,$salt)
Obviously something is missing as this is returning the same thing
$pwd = 'shits8888hjhfgnsdkjf8744884';
$salt = '50153fc193af9';
echo crypt($pwd,$salt)
The standard DES-based crypt() returns the salt as the first two characters of the output. It also only uses the first eight characters of str, so longer strings that start with the same eight characters will generate the same result (when the same salt is used).
http://php.net/manual/en/function.crypt.php
If you want more control over the algorithm used for hash, I suggest you take a look at mcrypt.
Note also that crypt() (despite the name) does not actually encrypt a string, it just generates a hash. If you are specifying the salt, which you are presumably also storing somewhere, you might do better with something like this:
function my_crypt ($string, $salt) {
return sha1($string.$salt); // ...or your hashing function of choice
}
Referenced from the manual:
The standard DES-based crypt() returns the salt as the first two characters of the output. It also only uses the first eight characters of str, so longer strings that start with the same eight characters will generate the same result (when the same salt is used).
Both entries have got the same first 8 characters and the same salt. so it must return the same result.
For example:
echo crypt('12345678xxxxx','50153fc193af9');
echo crypt('12345678yyyyyy','50153fc193af9');
will both return 50gyRGMzn6mi6
because they share the same salt and the same first 8 characters
Every encryption algorithm has got a limit, even md5 gets repeated at some point.

Help me understand pack(), openssl_random_pseudo_bytes() and mt_rand() for salting passwords

I'm building an application that will have a user base, and I'm at the point of securing the login. I'm fairly new to programming (and PHP,) but my efforts thus far have pointed to using Crypt() and a Blowfish hashed salt.
Before I go further, let me specify that I am not interested in phpass at this time.
Within the crypt() documentation, a user recently posted this:
<?php
$salt = substr(str_replace('+', '.', base64_encode(pack('N4', mt_rand(), mt_rand(), mt_rand(), mt_rand()))), 0, 22);
?>
It is intended for use on systems
where mt_getrandmax() == 2147483647.
The salt created will be 128 bits in
length, padded to 132 bits and then
expressed in 22 base64 characters.
(CRYPT_BLOWFISH only uses 128 bits for
the salt, even though there are 132
bits in 22 base64 characters. If you
examine the CRYPT_BLOWFISH input and
output, you can see that it ignores
the last four bits on input, and sets
them to zero on output.)
Note that the high-order bits of the
four 32-bit dwords returned by
mt_rand() will always be zero (since
mt_getrandmax == 2^31), so only 124 of
the 128 bits will be pseudorandom. I
found that acceptable for my
application.
I tested my server, and indeed mt_getrandmax() returns 2147483647. I tried poking around the documentation to understand what the above code really does--the pack() code N4 is for a 32-bit string (big endian byte order??) repeated 4 times... which I assume is why there's 4 mt_rand() arguments.
What I don't understand is why he replaces + with . and the purpose of 22 base64 characters (not that I fully understand what base64 is.)
It was recommended that I look into openssl_random_pseudo_bytes() for my random salt generation, as the previous method I was looking at was limiting itself to just 1234567890abcdefghijklmnopqrstuvwxyz.
Supposedly there was a bug pre 5.3.4 causing openssl_random_pseudo_bytes() to run painfully slow, occassionally causing timeout errors. I'm not sure if I should try to use openssl_random_pseudo_bytes() with Crypt() or something like the above method using mt_rand() and pack().
I'm trying to understand more how all these elements work, and what they are doing conceptually--rather than just using one without understanding it to achieve my goal; I'm trying to learn :P
Can someone help me understand the different elements at work here, or at least direct me to a knowledge base where I can read about it? I think the most eluding component is understanding the different formats/terminology (base64, ascii, hexdec, bit, byte, etc.) but also in the end, how to achieve a fairly secure salt for use with my passwords.
Let me start of by saying that there is nothing special about a salt from the standpoint of generation. It's just another random string. It's special in how it's used, but not generated.
Your specific questions
Why does he replace + with .?
I have no idea. Perhaps it's because the + character may be confused with a space in urls. But a salt should never be in a url, so that's likely not it.
What does base64/hexdec do:
Base64 converts a raw byte stream (each byte having values from 0 to 255) into a base 64 representation. There are plenty of resources on it, so it's not worth going deep into. Read the wikipedia article for more information.
hexdec converts a hex number (a-f0-9) into a decimal one. It converts from base 16 to base 10 (just another way of representing numbers).
What is a bit and byte:
A bit is a single unit of information. It has 2 states, 0 or 1. A Byte is a series of 8 bits. So a byte can have 256 unique combinations. Read Wikipedia...
What is ascii
It's a character set. It represents a single printable character in a single 8-bit byte. Again, I'd suggest reading Wikipedia.
Salts in General
The goal of a good salt generation function is large entropy. That means that the number of possible outputs is as large as possible. So any method should produce a large set of results.
Now, you need to define what are acceptable characters for the salt (since you'll need to store the salt to verify the hash). The best possible salts are full-byte numbers and not just displayable characters. Now, you won't be able to display this in a meaningful manor, but you don't need to display it. Plus, for storage, you could always use base64_encode it.
Next, you need to choose how big you want the salt to be. The bigger the salt is, the better. A 32 character salt is acceptable, but a 128 character salt is better. The size of the salt, and the number of options per character will dictate the number of possibilities there are. Some common combinations:
Hex, 32 characters: 2e38 possibilities
Hex, 128 characters: 1e154 possibilities
Full Byte, 32 characters: 1e77 possibilities
Full Byte, 128 characters: 1e308 possibilities
Now, you need to generate the salt. The key is to do as many random calls as necessary to fill out the entropy. You can do this a few ways:
System Dependent (only works on *nix but best entropy):
$f = fopen('/dev/urandom', 'r');
$seed = fgets($f, $characters); // note that this will always return full bytes
fclose($f);
Library dependent (good, but requires OpenSSL to be installed)
$seed = openssl_random_pseudo_bytes($characters);
fallback
$seed = '';
for ($i = 0; $i < $characters; $i++) {
$seed .= chr(mt_rand(0, 255));
}
Now, you need to convert it into the desired output format.
Hex (a-f0-9):
$out = '';
for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
$num = ord($seed);
$out .= dechex(floor($num / 16)) . dechex($num % 16);
}
Base36 (a-z0-9):
$out = '';
for ($i = 0, $len = strlen($seed); $i < $len; $i++) {
$num = ord($seed);
$out .= base_convert($num, 10, 36);
}
Base64 (a-zA-Z0-9+=):
$out = base64_encode($seed);
Full Byte:
Nothing is necessary since it's already in this format.

Categories