What do you use? What are the benefits of using it?
Gimme the lowdown on all the techniques, pros and cons all the tutorials don't tell you about.
The short answer is: you don't. What you actually store is the result of running the user's password through a one way hash-function. Before I would have thought using something like MD5 would be fine until I read this and would recommend looking at bcrypt instead since it can help against brute-force attacks.
Hashing is common in storing password. But they are all the same, just that the longer the hash result it produced, the harder it is to be hacked. The hashing result from the same hash function normally having the same length. Without restriction on the input text (which is unlimited in length), you might produce 1 same hash string from multiple sentences/words. This is where the hole lie. Read more about pigeonhole principle and Birthday Attack
I normally use MD5(). But it's out of the standard already I guess because some of the collision something. Somehow people invented a system that can detect 1 hashed string with
more than one real string.
use SHA instead. To make it more secured, you could add $salt on it, Make it a double protections, so, hash the actual password first, add the salt to the hashed password, then hash them again.
Remember, the longer the result string, the better it is.
some recommend bcrypt, but I never use it before.
You'll want to store the original password like this
md5($password)
Your login script schould be like this:
$sql = "SELECT `id` WHERE username=`$username` AND password=`" . md5($password) . "`";
$result = mysql_query($sql,$link);
if (mysql_num_rows($result) == 1) {
// user is authenticated
}
This should actually be more complicated, but for conceptualization its been simplified.
The simple but secured way of storing password is to use the MD5 encription.
md5($password);
will give the md5 encrypted value.
You can use a combination of two or more encryption algorithms.
For example:
md5(sha1($password));
or just like this:
md5(md5($password));
To keep things simple use md5 with a salt. MD5 is a one way hash. For example:
md5("hello") = "5d41402abc4b2a76b9719d911017c592"
So, without salts, you end up saving "5d41402abc4b2a76b9719d911017c592" into your database. Then when one tries to login by inputting their password you compare the md5 of the inputted password with the md5 you saved.
if (md5($input_password) == "5d41402abc4b2a76b9719d911017c592")
log_in_user();
However, md5 is insecure, as is because the md5 of x is always y. So, if one were to compromise your database and find the password listed as "y" then you know the password is "x" if you've created or downloaded a md5 lookup table.
Instead do something like:
$password_to_save_to_db = md5(md5($input_password . $date_user_registered));
Therefore each user will have their own unique salt and those lookup tables will be rendered useless. (It could be re-created for each password the hacker wants to steal, but that's much more time consuming and plus the double md5 makes things a little more difficult.
You can read more at http://sameerparwani.com/posts/using-salts-for-extra-security
Related
I'm developing a PHP web application that uses an LDAP server for authentication. However, in case the LDAP server goes down, or something else goes wrong and I still need to access the system, I want to have a master password written directly in the code.
The previous iteration of this system (which I did not write) simply stored the passwords as plaintext (!). So:
if ($username == "dan" && $password == "32fsss") {
which of course is extremely unsecure.
So I want to fix that. I was thinking of hashing the password, so doing
$hashedPassword = password_hash($password);
if ($username == "dan" && $hashedPassword == "HASHPASSWORDSTOREDHERE") {
So I would hard-code the hash for the master password, not the actual password itself. Is this secure? I know a stolen hash is bad, but it's better than a stolen plaintext password.
Of course all user-input would be appropriately sanitized.
Alternatively: any other thoughts for having a master-password access system?
If it's purely a read only incident and all they get is the hashed version, it shouldn't be an issue. That being said, I prefer to use the hash() function rather than password_hash(), but that's just my personal preference.
The only issue is if the attacker who reads the php file and obtains the hashed password has a way to reverse it (i.e. a rainbow table). In this instance, your system becomes insecure. This is where my preference for hash() over password_hash() comes from. With hash(). To generate the hashed form, I can say:
$HashedMasterPassword = hash('md5', $MasterPassword . "MyMD5Salt") . hash('sha256', $MasterPassword . "MyShaSalt");
Then in my uploaded code I can take the output of the above code ($HashedMasterPassword) and hard code that string into my code. This adds two different salts, two different hashing functions and it reduces the already low chance of a rainbow table attack because the attacker would need a collision that occurs for both md5 + the first salt and sha256 + the second salt. This is, I dare say the word, impossible. Sure, pigeon hole effect says it's possible and it is, but this is SO near impossible that I wouldn't fret over it.
That all said, using password_hash() and storing that hash is fine if you really want to. I just personally dislike it.
EDIT: ADDING ON IMPLEMENTATION DETAILS
I believe based on the comments that there was some confusion as to how this would be implemented. Let's say my master password was thisIsMyPassword (Super secret and super secure, I know). Well, we'd need to generate the hard coded hashed form. The hard coded hashed form would be found by saying:
$MasterPassword = "thisIsMyPassword";
$HashedMasterPassword = hash('md5', $MasterPassword . "MyMD5Salt") . hash('sha256', $MasterPassword . "MyShaSalt");
echo $HashedMasterPassword;
Important: You would NOT upload this to the server. This would just be used for generating the hash and it should then be permanently deleted.
The output of this would be: 6dcfc45fba2fff44b7cfc8df7245a1b7804c88d63a82ac2ba33e0de32ab7f20c5afe2c1784e87f1bae8a4f91f1a84833
So, in your code that requires a master password to override, you'd enter:
$MasterHash = '6dcfc45fba2fff44b7cfc8df7245a1b7804c88d63a82ac2ba33e0de32ab7f20c5afe2c1784e87f1bae8a4f91f1a84833';
$SubmittedMasterPass = $_POST['MasterPassword'];
$HashedVersionOfSubmittedMasterPass = hash('md5', $SubmittedMasterPass . "MyMD5Salt") . hash('sha256', $SubmittedMasterPass . "MyShaSalt");
if($MasterHash !== $HashedVersionOfSubmittedMasterPass){
die('Override Failed. Reason: Wrong Password.');
}else{
// User has successfully logged in using the master password.
}
Again to clarify based on information from the comments, this is more secure than just using SHA256 because a brute-forced master password must be found that produces 6dcfc45fba2fff44b7cfc8df7245a1b7 when MyMD5Salt is appended to the end of it and it also must produce 804c88d63a82ac2ba33e0de32ab7f20c5afe2c1784e87f1bae8a4f91f1a84833 when MyShaSalt is appended onto the end of it. This means the collision must satisfy two different algorithms which is much more difficult to do than satisfying just the Sha256 algorithm on it's own.
However, as noted in the comments, this does take a few more milliseconds to process; so, if decreasing execution time is a big priority to you, then it might be recommended to only use Sha256 on its own rather than using MD5 and Sha256 combined.
If the user has access to the source code. They can just change it, so it doesn't really matter.
One can discuss if such a "backdoor" is desirable or not, but if you cannot decide it for yourself, there are two ways to protect such a hardcoded password. Storing only the hash is surely much better than the plaintext password, because everybody with read-access to the code could otherwise use it on a running system.
The first possibilty is to use a slow hash algorithm with a cost factor, such as password_hash() which you proposed in your example. If an attacker has read-access to the code, he would have to brute-force this password, to use it on a live system. A slow hash algorithm will thwart brute-forcing, while MD5 and other functions are ways too fast (8 Giga MD5 per second). The drawback is a slowdown in your application when the password is verified.
The second possiblity is a very strong password. Choose a long enough random combination, e.g. 70 characters generated with a password-manager and use it as the master key. Even fast algorithms like SHA256 are out of scope for brute-forcing then, and your key is immune against dictionary attacks and does not even need salting.
I just recently began looking into password encryption.
My current code looks like this :
<?php
define('salt','7hPqMO(m=F+!!L6(#Yhp-CdF, &Q}+cIrA;c#wcP(E--V<qRmq!v*aSnM;H=4cD0');
$password = "CatsRsoCool47";
$myHash = hash( 'whirlpool',salt.$password );
echo $myHash;
?>
How would I check if the user inputed the correct password? I assume there is some sort of built in function that takes the parameters of the salt,hash and the encryption method and returns a boolean.
Also, what is the safest way of generating a salt? The current salt I have is static and the same for every user. Should I do something like
$salt = Time()+'7hPqMO(m=F+!!L6(#Yhp-CdF, &Q}+cIrA;c#wcP(E--V<qRmq!v*aSnM;H';
If it was done like so, wouldn't I have to store the timestamp in the database per user. Isn't that a security flaw?
I am lost.... Please suggest the best way to hash and check passwords. A script would be nice to look at :) This question might also be a possible duplicate, if so I'm very sorry
Basically, when a password comes in, whatever you did to store the password, you use the same thing applied to the incoming password.
You can then check that the two salt+hash values are the same.
It's as simple as that - do the same thing to the two passwords and you should get the same result.
You're right to be worried about using the same salt every time. What you really want to do it to use a different salt each time. You can then store the salt alongside the password. It sounds counter-intuitive, but this is perfectly OK. Having the salt doesn't allow you to reverse the hash as the process isn't reversible anyway.
Then, when you want to check a password, you look up the user, get their salt, use it to apply the hash and then check what you end up with against their stored hash.
For example (using a constant salt), you might have something like:
<?php
define('salt','7hPqMO(m=F+!!L6(#Yhp-CdF, &Q}+cIrA;c#wcP(E--V<qRmq!v*aSnM;H=4cD0');
$incomingPassword = $_POST['password'];
$storedHash = getStoredHash( $_POST['username'] );
$incomingHash = hash( 'whirlpool',salt.$incomingPassword );
if ( $incomingHash == $storedHash ) {
echo('Passwords match!');
}
?>
Hopefully it's easy to see how you might use this technique with a moving salt.
Note - this is not encryption - the whole point is that the method is one way. You generate something that cannot be used to retrieve the actual password. Encryption implies the process is reversible.
Attacks on hashed passwords are done by what's called a 'rainbow table'.
The attacker builds a table of possible password alongside their corresponding hashes using the same technique that you use. This table is then compared against your stored passwords. When there's a match the attacker can then infer the password that was stored for that row.
Having a single salt makes this easier as the technique is identical for every password. If you use a different salt per row then the technique has a random factor meaning the attacker needs a MUCH bigger rainbow table to attack you with - essentially a full sized table per row.
I'm making a registration form, my only doubt is how to handle passwords (how to insert them into MySQL database). I don't have the slightest idea on how to do it, what type of column must Passwords be, whether I must encrypt them somehow, etc. Could you provide a basic example with explanation so that I manage to do it?
You don't want to store the password as-is in plaintext. You don't even want to be able to know what the password is. Therefore, you store a hash of the password in your database. When the user wants to log in, you hash the password he's trying to login with, then compare that to the hash in the database. Any serious password storage system furthermore salts the hash to prevent rainbow table attacks against the password (google that). Since this is a rather complex topic and you apparently have no experience with it at all, I recommend you use phpass to hash and salt your passwords without worrying about the implementation details. The phpass site also has some good introductory articles about the topic. Here's another one that keeps it really simple.
As for the database, that'll just be a normal VARCHAR field long enough to hold the hash.
Read this: http://codahale.com/how-to-safely-store-a-password/
Then do this: http://www.openwall.com/phpass/
You should not store password, password hash only.
Database type should be choose after you will choose hasfunction.
For md5/sha512 it will be char(32) if you will keep hex representation
Query is something like this:
"INSERT INTO users SET otherFields,pass_hash='".hashFunc($_POST['password']."';
where hashFunc generates hash ex
function hashFunc($pass){
$salt='something';
md5($salt . $pass);
}
The only way to safely secure a password is using a Moore's Law-defeating hash function. Use bcrypt!
One of the ways it can be done is by using md5. You convert the password to md5 and put it in the database (md5 encryption is one-way) when the user logs in again you convert the filled in password again and check if the converted password is somewhere to be found in your database (in combination with a username usually).
EDIT
You can make a string into an md5 string with this:
$converted_pass = md5($unconverted_pass);
However you will need to add a so called salt-key to the password before you encrypt it with md5. This is a set of letters/numbers etc. If you do this every time you will have the same result but it will be quite safe :)
I have been searching around and I am still unsure of what a "salt" is and how to use/implement it. Sorry for the noobish question, I am self learning php.
I am definitely not an expert, but the really short answer is that "salting" a line of text means to stick a few extra characters on the end of it. You could salt "salt" with "abcdefg" to get "saltabcdefg". This might be useful if "salt" happens to be a password that you'd like to make more difficult to guess.
Typically, the password+salt are transformed ('hashed') by some difficult-to-reverse process into a completely different string. This transformed string is then stored as the password, together with the plaintext of the salt, and the original plain text of the password proper is tossed away. When you want to check that someone has input the correct password, you combine whatever they've typed in with the salt that's listed in the password file and then hash the result. If the result matches the password hash you have on record, then you know that they've put in the right password.
Implementing a salt can be as easy as picking a string to serve as the salt and then making sure you keep track of it. But, you could vary the salt with each password, and then you'll have to have a way of keeping track of password+salt combinations as well as generating the variations. Of course, you'll probably also want to hash the password rather than saving the password's plain text, and so you'll have to pick a hash function. At this point, the problem has proceeded from salting proper to implementing a password security scheme.
For PHP, you might want to look at how some of the frameworks have implemented this. Two quick links, for CakePHP and Zend, respectively:
http://www.jotlab.com/2010/04/18/cakephp-rainbow-table-protection-behaviour/
http://www.zimuel.it/blog/2009/07/build-a-secure-login-with-zend-framework/
When I first asked this question, many years ago, I was asked in response, "What does salt do for food?" The answer is that it adds variety to food. The idea behind cryptographic salt is that it's something you add to the end or beginning of a string in order that two passwords that are identical don't hash to the same cryptographic value.
Consider this - if I had a password that was really common, like 'hello123', and then it hashed to the exact same cryptographic hash as all other 'hello123' passwords, couldn't I just look in the list of hashed passwords to see who else had the same cryptographic hash, and use my password on their account?
A salt is a (short) string that is added to the string you want to encrypt or hash. An Example:
<?php
$password = 'abcdefg';
$salt = 'anythingyouwant_';
$pw_hash = md5($salt.$password);
?>
This adds security to the hash, as it's unlikely that "anythingyouwant_abcdefg" is already stored in a hash-database ( http://en.wikipedia.org/wiki/Rainbow_tables )
Well its in the comments, thanks ceejayoz
http://en.wikipedia.org/wiki/Salt_(cryptography)
A salt is something you add to a string before you hash it, it adds another layer of security to passwords and the like.
Let us spice up things a little by combining several algorithms for hashing, making a double hashing algorithm:
$password = "myPassword";
$salt = sha1(md5($password)).'k32duem01vZsQ2lB8g0s';
$password = md5($password.$salt);
As you can see, we first hashed the password using double hashing algorithm (md5 and sha1) and concatenating with a key created salt value. After that, we combined real password with generated salt value and hashed it again with md5. The advantage is that this way alt value is random and it changes, making it nearly impossible to break. I mean, if you can wait for a million years and have a super computer on your hands, try to break it.
For some reason. Salts are usually hard for people new to cryptography to grasp. Once it clicks though, the concept is extremely simple. Have a look at this article. I think it explains the concept better than most.
https://web.archive.org/web/20140430053616/http://cryptodox.com/Salt_(cryptography)
I was reading this tutorial for a simple PHP login system.
In the end it recommends that you should encrypt your password using md5().
Though I know this is a beginners' tutorial, and you shouldn't put bank statements behind this login system, this got me thinking about encryption.
So I went ahead and went to (one of the most useful questions this site has for newbies): What should a developer know before building a public web site?
There it says (under security) you should:
Encrypt Hash and salt passwords rather
than storing them plain-text.
It doesn't say much more about it, no references.
So I went ahead and tried it myself:
$pass = "Trufa";
$enc = md5($pass);
echo $enc; #will echo 06cb51ce0a9893ec1d2dce07ba5ba710
And this is what got me thinking, that although I know md5() might not the strongest way to encrypt, anything that always produces the same result can be reverse engineered.
So what is the sense of encrypting something with md5() or any other method?
If a hacker gets to a password encrypted with md5(), he would just use this page!.
So now the actual questions:
How does password encryption work?
I know I have not discovered a huge web vulnerability here! :) I just want to understand the logic behind password encryption.
I'm sure I'm understanding something wrong, and would appreciate if you could help me set my though and other's (I hope) straight.
How would you have to apply password encryption so that it is actually useful?
What about this idea?
As I said, I may/am getting the whole idea wrong, but, would this method add any security in security to a real environment?
$reenc = array(
"h38an",
"n28nu",
"fw08d"
);
$pass = "Trufa";
$enc = chunk_split(md5($pass),5,$reenc[mt_rand(0,count($reenc)-1)]);
echo $enc;
As you see, I randomly added arbitrary strings ($reenc = array()) to my md5() password "making it unique". This of course is just a silly example.
I may be wrong but unless you "seed the encryption yourself" it will always be easily reversible.
The above would be my idea of "password protecting" and encrypted password, If a hacker gets to it he wont be able to decrypt it unless he gets access to the raw .php
I know this might not even make sense, but I can't figure out why this is a bad idea!
I hope I've made myself clear enough, but this is a very long question so, please ask for any clarification needed!
Thanks in advance!!
You should have an encryption like md5 or sha512. You should also have two different salts, a static salt (written by you) and then also a unique salt for that specific password.
Some sample code (e.g. registration.php):
$unique_salt = hash('md5', microtime());
$password = hash('md5', $_POST['password'].'raNdoMStAticSaltHere'.$unique_salt);
Now you have a static salt, which is valid for all your passwords, that is stored in the .php file. Then, at registration execution, you generate a unique hash for that specific password.
This all ends up with: two passwords that are spelled exactly the same, will have two different hashes. The unique hash is stored in the database along with the current id. If someone grab the database, they will have every single unique salt for every specific password. But what they don't have is your static salt, which make things a lot harder for every "hacker" out there.
This is how you check the validity of your password on login.php for example:
$user = //random username;
$querysalt = mysql_query("SELECT salt FROM password WHERE username='$user'");
while($salt = mysql_fetch_array($querysalt)) {
$password = hash('md5',
$_POST['userpassword'].'raNdoMStAticSaltHere'.$salt[salt]);
}
This is what I've used in the past. It's very powerful and secure. Myself prefer the sha512 encryption. It's actually just to put that inside the hash function instead of md5 in my example.
If you wanna be even more secure, you can store the unique salt in a completely different database.
Firstly, "hashing" (using a cryptographic one way function) is not "encrypting". In encryption, you can reverse the process (decryption). In hashing, there is (theoretically) no feasible way of reversing the process.
A hash is some function f such that v cannot be determined from f(v) easily.
The point of using hashing for authentication is that you (or someone seeing the hash value) do not have any feasible way (again, theoretically) of knowing the password. However, you can still verify that the user knows his password. (Basically, the user proves that he knows v such that f(v) is the stored hash).
The weakness of simply hashing (aside from weak hash functions) is that people can compile tables of passwords and their corresponding hash and use them to (effectively) get the inverse of the hash function. Salting prevents this because then a part of the input value to the hash is controlled and so tables have to be compiled for that particular salt.
So practically, you store a salt and a hash value, and authenticate by hashing a combination of the salt and the password and comparing that with your hash value.
MD5 is a one way hashing function which will guard your original password more or less safely.
So, let's say your password is "Trufa", and its hashed version is 06cb51ce0a9893ec1d2dce07ba5ba710.
For example, when you sign in to a new webpage, they ask you for your username and password. When you write "Trufa" as your password, the value 06cb51ce0a9893ec1d2dce07ba5ba710 is stored in the database because it is hashed.
The next time you log in, and you write "Trufa", the hashed value will be compared to the one in the database. If they are the same, you are authenticated! Providing you entered the right username, of course.
If your password wasn't stored in its hashed form in database, some malicious person might run a query somehow on that database and see all real passwords. And that would be compromising.
Also, since MD5 is a 128 bit cryptographic function, there are 2^128-1 = 340282366920938463463374607431768211455 possible combinations.
Since there are more possible strings than this, it is possible that 2 strings will generate the same hash value. This is called a collision. And it makes sure that a hashed password cannot be uniquely reverse engineered.
The only vulnerability with salting is that you need to know what the salt is in order to reconstruct the hash for testing the password. This is gotten around by storing the entry in the authdb in the form <algorithm>$<salt>$<hash>. This way the authdb entry can be used by any code that has access to it.
You're missing the important step - the salt. This is a unique (per user, ideally) bit of extra data that you add to the password before hashing it.
http://en.wikipedia.org/wiki/Salt_%28cryptography%29
Your idea (salting) is well known and is actually well-implemented in the PHP language. If you use the crypt() function it allows you to specify a string to hash, a method to encrypt (in some cases), and a salt. For example,
$x = crypt('insecure_password', $salt);
Returns a hashed and salted password ready for storage. Passwords get cracked the same way that we check if they're right: we check the hash of what the user inputs against the hash of their password in the database. If they match, they're authenticated (AFAIK this is the most common way to do this, if not the only). Insecure passwords (like password) that use dictionary words can be cracked by comparing their hash to hashes of common passwords. Secure passwords cannot be cracked this way, but can still be cracked. Adding a salt to the password makes it much more difficult to crack: since the hacker most likely doesn't know what the salt is, his dictionary attack won't work.
For a decent hash the attacker won't be reversing the hash, they'll be using a rainbow table, which is essentially a brute-force method made useful if everyone uses the same hash function.
The idea of a rainbow table is that since hashing is fast I can hash every possible value you could use as a password, store the result, and have a map of which hash connects to which password. If everyone just takes their passwords and hashes them with MD5 then my hash table is good for any set of password hashes I can get my hands on!
This is where salting comes in. If I take the password the user enters and add some data which is different for every user, then that list of pre-determined hashes is useless since the hash is of both the password and some random data. The data for the salt could be stored right beside the password and even if I get both it doesn't help me get the password back since I still have to essentially brute force the hash separately for every single user - I can't form a single rainbow table to attack all the hashes at once.
Of course, ideally an attacker won't get the list of hashed passwords in the first place, but some employees will have access so it's not possible to secure the password database entirely.
In addition to providing salt (or seed), the md5 is a complex hashing algorithm which uses mathematical rules to produce a result that is specifically not reversable because of the mathematical changes and dataloss in throughput.
http://en.wikipedia.org/wiki/Cryptographic_hash_function
md5 (or better put: hash algorithms in general) are used to safely store passwords in database. The most important thing to know about hashes is: Hashes are not encryptions per se. (they are one-way-encryptions at most). If you encrypt something, you can get the data back with the key you used. A hash generates a fixed-length value from an arbitrary input (like a string), which can be used to see if the same input was used.
Hashes are used to store sensitive, repeatly entered data in a storage device. Doing this, nobody can recreate the original input from the hash data, but you can hash an incoming password and compare it to the value in the database, and see if both are the same, if so, the password was correct.
You already pointed out, that there possibilites to break the algorithm, either by using a database of value/hash pairs or producing collisions (different values resulting in the hash value). You can obscure this a bit by using a salt, thus modifying the algorithm. But if the salt is known, it can be used to break the algorithm again.
I like this question. But I think you've really answered yourself.
The site you referenced uses dictionary lookups of known, unsalted, md5's - it doesn't "crack" anything.
Your example is almost good, except your application needs to be able to regenerate the md5 using the same salt every time.
Your example appears to use one of the random salts, which will fail 2 of 3 times if you try to compare a users password hash to something input.
People will tell you to also use SHA1 or SHA256 to be have a 'stronger' hash - but people will also argue that they're all 'broken.'
That documentation is misleading -- it teaches a "vulnerable" concept and presents it as somehow being "secure" because it (the saved password) looks like gibberish. Just internet junk that won't die. The following link should clear things up (you have already found a good bit of it though, it seems. Good work.)
Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes talks about MD5 (and why it should not be used) along with salt (e.g. how to thwart rainbow attacks) as well as provides useful insights (such as "Use someone else’s password system. Don’t build your own"). It is a fairly good overview.
This is my question about the aspects of md5 collision, slightly related to your question:
Is there any difference between md5 and sha1 in this situation?
The important part is in the first 3 rows, that is: you must put your salt before the password, if you want to achieve stronger protection, not after.
To simply answer the title of your question, md5's only real use nowadays is for hashing large strings (such as files) to produce checksums. These are typically used to see if both strings are identical (in terms of files, checksums are frequently used for security purposes to ensure a file being distributed hasn't been tampered with, for example).
To address each of your inline questions:
How does password encryption work?
How would you have to apply password encryption so that it is actually useful?
Secure password hashing works by taking the password in plain text form, and then applying a costly hashing function to it, salted with a cryptographically secure random salt to it. See the Secure hash and salt for PHP passwords question for more detail on this.
What about this idea?
Password hashing does not need to be complicated like that, and nor should it be. Avoid thinking up your own algorithms and stick with the tried and tested hashing algorithms already out there. As the question linked above mentions, md5() for password hashing has been obsolete for many years now, and so it should be avoided.
Your method of generating a "random" salt from an array of three different salts is not the randomness you're looking for. You need unique randomness that is suitable for cryptographically secure (i.e. using a cryptically secure pseudo-random number generator (CSPRNG)). If you're using PHP 7 and above, then the random_bytes function can be used to generate a cryptographically secure salt (for PHP 5 users, the random_compat library can be used).