Code:
echo password_hash("stackoverflow", PASSWORD_DEFAULT, ['salt' => 'twenty-one-characters'] );
Result:
Warning: password_hash(): Provided salt is too short: 21 expecting 22
code:
echo password_hash("stackoverflow", PASSWORD_DEFAULT, ['salt' => 'twenty-one-charactersA'] );
Result:
$2y$10$dHdlbnR5LW9uZS1jaGFyYOVyX13hK9eb4/KXMAkHsAJX..YR7t/32
code:
echo password_hash("stackoverflow", PASSWORD_DEFAULT, ['salt' => 'twenty-one-charactersB'] );
$2y$10$dHdlbnR5LW9uZS1jaGFyYOVyX13hK9eb4/KXMAkHsAJX..YR7t/32
Question:
As you see, by appending A and B to 21 character strings we created two different salts of 22 characters, but, the HASHES are same! That is the 22nd character is ignored? If it is ignored then why does it ask for 22 character salt?
BCrypt expects a salt of a given alphabet: ./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz. As you can see the '-' is not in it and that's why your salt is invalid. A valid salt you could see plaintext in the hash-value.
In most cases it is best to omit the salt parameter. Without this parameter, the function will generate a cryptographically safe salt, from the random source of the operating system.
password_hash("stackoverflow", PASSWORD_DEFAULT);
Nevertheless you are right, when you say that BCrypt does not use the full 22 characters. It seems that BCrypt only uses 126 bits of the salt instead of the 128bits you get with 22 base64 encoded characters. For more information you can have a look at this discussion Why does crypt/blowfish generate the same hash....
First, please don't provide your own salt. You're not going to do a better job generating it than the library does. And using static salts (like you did in the example) will compromise security. Just let it generate its own salt (incidentally, I believe letting a salt in is the biggest mistake I made in that API).
As far as 21 vs 22 characters, give this answer a read.
Basically, the salt is base64 encoded. This means that every 6 bits of the salt is encoded into 8 bits. So every byte of encoded salt is 6 bits.
21 characters is 126 bits. That means that only part of the 22nd character is used (the first 2 decoded bits). The reason you get the same hash with A and B, is that the leading 2 bits are the same for both characters.
In fact, there are only 4 unique hashes for the 22nd byte.
Related
I am using AES 256 cbc method to encrypt my files.
The column which I am encrypting is called 'Name'.
previously before encrypting I had set the varchar length in phpmyadmin for 'Name' to be 20. when I was trying to encrypt , I saw it was short and the entire encrypted string was not getting inserted in the database.
So I changed the size of varchar to 50 but still the length is small.
I have to do this for other column as well. How do I determine efficient length for 'Name' column.
I am using randomized IV in the encryption as can be seen from the below example.
$encryptionMethod = "AES-256-CBC";
$secretHash = "25c6c7ff35b9979b151f2136cd13b0ff";
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($encryptionMethod));
//To encrypt
$encrypted = openssl_encrypt($textToEncrypt, $encryptionMethod, $secretHash,false,$iv);
$encryptedMessage = $encrypted . ':' .base64_encode($iv);
during decryption I use
$parts = explode(':', $encryptedMessage);
// Decrypt the data
$decryptedMessage = openssl_decrypt($parts[0], $encryptionMethod, $secretHash, 0, base64_decode($parts[1]));
echo $decryptedMessage;
since the IV is appended to the encrypted string , how would I be able to calculate the length needed to be defined in the database for the column 'Name'.
The block size of AES is 16 bytes, so you you'll need
the size of your input, rounded up to the closest multiple of 16
plus, if the input is already a multiple of 16, one block size for the PKCS#5 padding
plus 16 bytes for the IV
Note that this doesn't necessarily apply to other cipher modes1.
So for 20 bytes of input you'll need a total of 48 bytes. However, you are also base64 encoding the result, which requires at least 33% more space (i.e. you should be storing the raw bytes if you care about space).
You should always concatenate before encoding, otherwise you often waste space with multiple padding byte sequences. If your input is 20 bytes long, encoding the 32 byte ciphertext by itself produces 44 bytes, and encoding the IV produces 24 bytes (both need padding). Concatenating before encoding produces only 64 bytes.
Concatenating before encoding also doesn't require the delimiter, because the length of the IV is known.
1 AEAD ciphers, such as GCM, are generally preferable over CBC, but require more space for storing the authentication hash, obviously.
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.
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.
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.
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!