This question already has answers here:
How do you use bcrypt for hashing passwords in PHP? [duplicate]
(11 answers)
Closed 9 years ago.
I have been looking into replacing SHA1 as the encryption of passwords with possibly bcrypt or something similar, and I cant seem to find a step-by-step, easy to follow tutorial for implementing this.
I did a quick tutorial on youtube which produced the following code:
$username = 'myusername';
$password = 'pa55w0rd';
$str = substr($username, 0, 6);
$salt = '$2a$12$rU8E3fsI9rsKh3V2'.$str.'$';
$pass = crypt($password, $salt);
echo $salt . '<br>' . $pass;
and when I run the code in the browser, this get output:
$2a$12$rU8E3fsI9rsKh3V2myuser$
$2a$12$rU8E3fsI9rsKh3V2myuseeMSOT1BADLFs/ncqHx5aG2q953uqp.Tu
QUESTION 1:
Am I correct in assuming that both strings are generated for a user, and that both strings are required to be stored in, for example, the users table as columns "salt" and "pass"?
QUESTION 2:
Why does it look like part of the username is visible within the salt and the pass? Is this normal, or is there some additional step I need to take to eliminate this from happening?
QUESTION 3:
Is this approach to hashing passwords more secure than md5 and sha1, or is there a better approach that I should be using?
Any suggestions appreciated..
I'd recommend using PHP's new password_hash and password_verify functions.
As you probably don't have PHP >= 5.5.0, there's a PHP implementation that adds support for the upcoming functions to older versions of PHP.
More info: https://gist.github.com/nikic/3707231
No, The salt is stored as part of the hashed password. Note that hashing is done against rainbow attacks (pre-prepared hash dictionary) or similar.
In order to check for valid password, the crypt function can take the hashed password itself as the salt, for it know how to split the salt from the hash.]
See: Comparing passwords with crypt() in PHP
No, that's fine, see above.
SHA1 Is better and stronger than MD5, Although it is preferable to use SHA-2.
Related
update: It appears that what I'm naively trying to do here, of manually appending the salt to the password, will not work because of the way the crypt algorithm works (see Unix crypt using SHA-256 and SHA-512), so this is now really not a Stack Overflow question, but a purely Server Fault question on MySQL 8 functions. Leaving this here, in case anyone else has this problem. You can see the question on Server Fault here.
There's a long backstory here, but the short story is, I can't seem to get MySQL 8 and Dovecot to generate the same SHA256 hashes, given the same salt and password. I suspect either a trivial error, or some string encoding problem (or possibly both).
NB I asked this same question on Server Fault, as it seemed more appropriate for there, but it's not getting any love, and I see several similar questions here, so ...
Dovecot:
$doveadm pw -s SHA256-CRYPT -p apassword
{SHA256-CRYPT}$5$h1JEsg1tmnTGS9Ub$Saoi1jr/uddYVD.n5p0hz70H9slnubpG7MQCkzpAiu4
Then, I grab that salt (h1JEsg1tmnTGS9Ub), and try and get the same output from MySQL 8:
SELECT CONCAT('$5$', 'h1JEsg1tmnTGS9Ub', '$', TO_BASE64(UNHEX(SHA2(CONCAT('apassword', 'h1JEsg1tmnTGS9Ub'), 256)))) WHERE true
$5$h1JEsg1tmnTGS9Ub$Vm9gPbWHuXt/zslurPQ7Nx0JLp1CphlBQbnL9R86XbM=
As I mentioned, there seem to be several similar quesitons here, but either they're with MySQL 5.x using the ENCRYPT function, which has been removed in 8.0.3, or people seem to have decided to drop the salt and just use SHA512 unsalted. It seems to me that this should be easy, and yet ...
Anyone have any ideas on how to get MySQL 8 to generate the same hash as doveadm, with same password and salt?
FWIW, I've tried the MySQL with and without TO_BASE64 and with and without UNHEX (I believe in all combinations).
Thanks!
I'm currently working on a school-project and part of the task is to enable user login. Therefore I thought that using a simple base64 encoding with an conversion to lower case afterwards would be a pretty good encryption, because base64 normally consists of upper and lower characters. Here's a representational code:
set #passwd = 'Password';
set #salt = 'Salt';
set #email = 'tmp#gmail.com';
INSERT INTO `db_scrumboardtable`.`tb_user` (`mail`, `password`, `nameToDisplay`) VALUES (#email, LOWER(to_base64(sha2(concat(#passwd,#salt),512))) , 'test');
select u.tb_User_id from `db_scrumboardtable`.`tb_user` u where u.mail = #email and u.password = LOWER(to_base64(sha2(concat(#passwd,#salt),512)));
It's not secure and it's not encryption.
If you change all the base64-encoded chars to lower case, you'll lose the ability to get back the original value ("decrypt"). Since passwords should not be encrypted, but hashed, this is not so bad.
Now, thinking about why we're hashing passwords, the usual attack scenario is that an attacker successfully manages to steal the hashed password list, but in order to get those passwords they need to brute-force the input to find a match. In this case, they can simply decode the "encrypted" password and get a valid password out. Of course, this password has limited usefulness, because it can be used to log in to the server it was stolen from, but not necessarily to some other service the user might have used the same password for.
Passwords should be properly hashed with schemes like PBKDF2, bcrypt, scrypt or Argon2 with high cost factor or many iterations.
This isn't close to secure. Your injective transformation (mixed case to lower case) makes passwords easier to guess, not harder.
Please don't, in the 21st century riddled with cybercrooks, try to invent your own security software. Please read this. http://php.net/manual/en/faq.passwords.php
It's just a curiosity. If you encrypt a password (using sha1 or other methods) before inserting it in a query, it must be anyway sanitized? Or the hash's result is always safe?
This simple code are safe?
$salt = "123xcv";
$password = $_POST['password'];
$password = sha1($password+$salt);
$query = "select * from user where password='$password'";
Unless you validated the input somehow you shouldn't assume that it will always return a safe output because functions such as SHA1 can return error values if given unexpected input. For example:
echo '<?php echo sha1(''); ?>' | php
Warning: sha1() expects at least 1 parameter, 0 given in - on line 1
And this output obviously violates the assumption that "it's always a hex string". Other hashing functions in other languages can present yet another behaviour.
Apart from that, the above password hashing code scheme ($password = sha1($password+$salt);) is very weak (see why) and I would strongly recommend not using it even in an example as someone is eventually guaranteed to find it on StackOverflow and use in production.
Also, as already noted above, building SQL queries by concatenating strings is also a bad practice and can lead to security issues in future: today the only parameter in the query will be the password, tomorrow someone decides to add some other option and I bet they won't rewrite the query but just use the template that is already there...
This sql injection question question is asked out of a common delusion.
In fact, there is no such thing like "sanitization" at all, nor any function to perform such non-existent task. As well as there is no "safe" or "unsafe" data. Every data is "safe", as long as you're following simple rules.
Not to mention that a programmer have a lot more important things to keep in mind, other than if some particular piece of data is "safe" in some particular context.
What you really need, is to avoid raw SQL for such silly queries at all, using an ORM to run SQL for you. While in such rare cases when you really need to run a complex query, you have to use placeholders to substitute every variable in your query.
From the documentation:
The value is returned as a string of 40 hex digits, or NULL if the argument was NULL.
Assuming you have a large enough varchar column, you have no sanitization to do.
This being said, it's always cleaner to use prepared statements, there's no reason to just concat strings to build queries.
i am currently developing a API for a service and was wondering if this could be classed as safe enough to prevent injection and/or other malicious attacks to the databases.
$username = mysql_real_escape_string(ereg_replace("[^A-Za-z0-9]", "", $_REQUEST['username']));
$password = mysql_real_escape_string(ereg_replace("[^A-Za-z0-9]", "", $_REQUEST['password']));
What this is doing is stripping out everything but letters and numbers and then running the mysql_real_escape_string command to run a fine comb in case something managed to get though.
Skip the deprecated ereg_replace() function and just use mysql_real_escape_string().
Also, why would you want to limit the user's password to a subset of chars. This just makes breaking in much easier.
Just your regex would be enough, without any further cleaning.
However, you should consider creating some sort of layer between your forms and the database to do this cleaning automatically.
Nathaniel,
Should be fine your usernames (maybe add '_' to the RE) but you've got a real problem with passwords, haven't you? Any half decent authentication actively encourages a user to choose a password which contain symbol(s), as well as letters, UPPERCASE LETTERS, and numbers.
So I guess I'd just stick to using mysql_real_escape_string - Escapes special characters in a string for use in an SQL statement ... after CAREFULLY reading the documentation, of course.
Cheers mate. Keith.
I have read a post here that utf8_bin gives us more accuracy on comparing characters while utf8_general_ci does not.
I wonder - if I have a table that stores usernames and passwords, and I need them to be exact or correct when the user logs in my website.
Then should I use utf8_bin for this purpose?
Thanks.
EDIT:
By the way, this is the hash function I use to has the password,
function hash_sha512($phrase,&$salt = null)
{
//$pepper = '!##$%^&*()_+=-{}][;";/?<>.,';
if ($salt == '')
{
$salt = substr(hash('sha512',uniqid(rand(), true).PEPPER_KEY.microtime()), 0, SALT_LENGTH);
}
else
{
$salt = substr($salt, 0, SALT_LENGTH);
}
return hash('sha512',$salt.PEPPER_KEY.$phrase);
}
You're talking about the collation -- it's the characters the MySQL table will support. The "_ci" on a collation indicates that the collation is Case Insensitive. Meaning, "a" == "A" while in a case sensitive collation the example would evaluate to being false.
So yes, choosing a collation that is case sensitive will provide better accuracy. You can store the values using a case insensitive collation, but set a particular one for the query evaluation using the COLLATE function.
First on the issue of password storage... Since you seem to be using PHP (from your question history)... Salted sha1() hashes just won't cut it in a world where renting a few AWS instances to compute fast rainbow tables... sha1() is too fast.
Instead of trying your hand at do-it-yourself cryptology, why not trust libraries made by actual experts in the field? Use the Portable PHP password hashing framework.
PHPass actually uses bcrypt, which is an algorithm designed to prevent rainbow table, dictionary and brute force attacks. You can initialize it with a number of rounds: the higher the rounds, the longer it takes to compute the hash. That way, you can create stronger hashes if processing power increases.
Using it is simple:
require('PasswordHash.php');
$phpass = new PasswordHash(12, false); // Initiate for 12 rounds, using bcrypt
// Hash a password
$hash = $phpass->HashPassword('my secret password');
// Compare an hash to a given password
$formSupplied = 'hello world';
$isRight = $phpass->CheckPassword($formSupplied, $hash);
if($isRight) echo "Good";
else echo "Wrong";
Now on the subject of usernames... Store them using a _bin collation (ie.: utf8_bin). This will force MySQL to binary compare when during a WHERE and effectively makes your usernames case-sensitive.
HOWEVER, since this is UTF-8, it is going to be important to normalize the username before inserting and querying your data. Different operating systems represent accented characters in different ways. PHP has the intl extension which has a facility for UTF-8 normalization. The following should do:
$_POST['username'] = Normalizer::normalize($_POST['username']);