hash() function in mysql version 8.0 - mysql

I am trying to store passwords in my user table. Of course I want to salt and hash them before.
But there's no hash() method in mysql anymore. How can I do it in version 8.0? Are there alternatives?
Thanks in advance for your help.

You should hash passwords in your app, before sending them to SQL.
Why do I say this? After all you could do this:
INSERT INTO Accounts (user, salt, password)
VALUES ('myuser', '1234', SHA2(CONCAT('xyzzy', '1234'), 256))
But now you the password 'xyzzy' appears in plain-text in your query logs and binary logs, even if it is stored in hashed form in the table itself. If you don't secure your logs adequately, a hacker could get a hold of them and find everyone's password.
Instead, perform the hash operation in your application code. Get the result of that, and save the hash string verbatim in the database.
INSERT INTO Accounts (user, salt, password)
VALUES ('myuser', '1234', 'd3822b5f03ad0c1a363d874238f6b48fd68a131cc35d5e55c77a81db1d266b84')
That way the plain-text password does not get logged.
Likewise, when you do password checks for users as they log in, read the salt, then use it to hash the password they input, then compare that to the hash string stored in the database.
SELECT salt FROM Accounts WHERE user = ?
...calculate hash string using user input + salt...
SELECT password = ? AS password_matches FROM Accounts WHERE user = ?

Related

MySQL AES_DECRYPT in NodeJS, placeholder for encryption key?

I found similar replies but nothing really straightforward.
How can AES_DECRYPT be used only for the password field in a query using MySQL extension in NodeJS ?
What I have is as follow:
app.post("/verify",function(req,res){
connection.query('SELECT *, FROM `bosses` where u=? and p=?', [req.body.user,req.body.pass], function (error, results, fields) {
if(results.length){
session.loggedin=1;
res.redirect('/zirkus');
}else{
res.redirect('/soccer');
}
});
I assume that I need to modify the query with something like this:
connection.query('SELECT *, FROM `bosses` where u=? and p=AES_DECRYPT (?, 'ENCRYPTIONKEY')', [req.body.user,req.body.pass], function (error, results, fields) {
but somehow I can't get it to work properly. Should I use a placeholder for the encryption key too ?
EDIT
Thanks for the replies and explanation on why this was generally a bad idea :)
Here is a variation: no decryption password is stored in the code:
connection.query('SELECT *, AES_DECRYPT(p, ?) AS `key` FROM bosses WHERE u = ?', [req.body.pass, req.body.user], function (error, results, fields) {
console.log (req.body.pass + req.body.user )
if(results[0].key){
session.loggedin=1;
res.redirect('/zirkus');
}else{
res.redirect('/soccer');
}
});
});
Here the admin user types the decryption password in the form and if the decryption is successful (the key returns true) it allows the user to log in (without using or saving the password) else access is denied.
I assume that in this solution the only downside are the mysql logs right ?
Answer 1: Don't use encryption for storing user passwords. Use hashing.
There's no reason you need to decrypt user passwords, ever. Instead, when the user logs in, you hash their input with the same hashing function and compare the result to the hash string stored in the database.
Try bcrypt: https://www.npmjs.com/package/bcrypt
Also read https://blog.codinghorror.com/youre-probably-storing-passwords-incorrectly/
Answer 2: I never do encryption or hashing in SQL expressions. The reason is that the if you use the query log, it will contain the plaintext of the sensitive content, as it appears in SQL expressions. It will also be visible in the PROCESSLIST.
Instead, if you need to do encryption or hashing of sensitive content, do it in your application code, and then use the result in SQL statements.
Re your edit:
I assume that in this solution the only downside are the mysql logs right ?
No. The problem is that you're storing the password using reversible encryption. There is no reason to reverse a user password. If I visit a website that offers a "password recovery" feature where they can tell me what my password was (no matter how many other security checks they do), then I know they're storing passwords wrong.
If passwords are stored in a reversible encrypted format, this creates the possibility that someone else other than me can reverse the encryption and read my password. That will never happen with hashing, because you can't reverse hashing to get the original content.
If it is because of the logs ... ?
You could disable the query logs, of course. But there's also other places where the query is visible, such as:
the binary log (if you use statement-based binary logs)
the PROCESSLIST
the performance_schema statement tables
the MySQL network protocol. That is, if you don't use TLS to encrypt the connection between the application and the database, someone could intercept packets on the network and see the plaintext query with the plaintext content.
In your edited example, they could view the user's plaintext decryption key in any of the above contexts.
... why MySQL has this function ...?
There are legitimate uses of encryption other than user passwords. Sometimes you do need to decrypt encrypted content. I'm just talking about user passwords. User passwords can be authenticated without decryption, as I described at the top of this answer. It's covered in the blog I linked to, and also as a chapter in my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Another use of encryption and corresponding decryption function in SQL is when you develop code as stored procedures. It would be inconvenient to have to return encrypted data to the client application just to decrypt it, and then send it back to your stored procedures for further processing it.
You have to use doubole quotes for the decryption key or escaping ut
connection.query('SELECT *, FROM `bosses` where u=? and p=AES_DECRYPT (?, "ENCRYPTIONKEY)', [req.body.user,req.body.pass], function (error, results, fields) {
if(results.length){
session.loggedin=1;
res.redirect('/zirkus');
}else{
res.redirect('/soccer');
}
});
But as in every language passwords are usually only stored as hashed values, so that they can't be easily reconstructed, even with the logs. so chelkc for example https://coderrocketfuel.com/article/using-bcrypt-to-hash-and-check-passwords-in-node-js

How to use password() in sql?

I am trying to verify user login my matching the input password to the password input by user
My insert query:
insert into login (Emp_id, Emp_Fname, Emp_Lname, Username, Password) values (5, 'TestFName', 'TestLName', 'Test', password('april'));
it stores the password as this value :
*72B46CDA233C759A88BEF81F59F66D78B26B2848
select * from login where password = '*72B46CDA233C759A88BEF81F59F66D78B26B2848'; -- this line shows me the result
select password('april'); -- this returns *72B46CDA233C759A88BEF81F59F66D78B26B2848
select * from login where password = 'password(april)'; -- this returns an empty set
Is there any alternative to this line of code?
I think you need to use:
select * from login where password = password('april');
So, don't quote the whole password function, just the argument to the function.
One cannot safely store passwords with pure SQL commands, instead a dedicated password-hash function of the development language should be used. In PHP this would be the functions password_hash() and password_verify() for the verification of the password.
Even more, MySql's password() function was never intended to be used with user passwords and is deprecated (will be removed in future versions). Have a look at the second note box in the documentation.
The reason why you cannot left the hashing to the SQL command is, that salted password hashes cannot be searched for in the database. The searching has to be done by user name only and afterwards one can verify the found password hash with the user input. A more in-depth explanation you can find in this answer.
https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_password says:
This function is deprecated as of MySQL 5.7.6 and will be removed in a future MySQL release.
PASSWORD() is used by the authentication system in MySQL Server; you should not use it in your own applications.
That wasn't an idle warning. https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html#function_password says:
This function was removed in MySQL 8.0.11.
So don't use PASSWORD() — unless you plan to never upgrade to MySQL 8.0.
Besides that, you have some problems in your code.
insert into login (Emp_id, Emp_Fname, Emp_Lname, Username, Password)
values (5, 'TestFName', 'TestLName', 'Test', password('april'));
I wouldn't use password (or any other hashing function) in this way, because you still have the plaintext password in your SQL statement. This ends up getting logged in query logs and statement-based binary logs, so it's a security weakness. That is, anyone who can get access to your logs can inspect the passwords.
Instead, hash the password in your app, and then put the result of that hash into your SQL statement.
Which hashing function you use depends on the language you use to write your application code. #martinstoeckli mentions a couple of functions that are used by PHP developers, but those won't be the same for other programming languages. You don't mention which language you use.
Likewise, when you search for a login that has that password, it works if you search for a specific hash string, but this doesn't work:
select * from login where password = 'password(april)'; -- this returns an empty set
The reason is that you're searching for the string 'password(april)'. Putting an expression in quotes means to use that literal string — it won't execute the function and use the result of it.
Again, you don't want to calculate the hash using SQL anyway. That puts the plaintext password into query logs and is not good for security.
You want to produce the hash string in your app, and then use the hash string in searches, like your first example. But not using the PASSWORD() function — using some application code function.
select * from login where password = '*72B46CDA233C759A88BEF81F59F66D78B26B2848';
(The hash string above is based on your example. It's a hash produced by MySQL's PASSWORD() function, only as strong as a SHA1 hash, which is known to be unsuitable for passwords.)
Actually, my preferred method is not to search for a password at all. Search for the login, and return the password hash string that is stored in the database.
select password from login where user = 'billkarwin'
Then in the application code, compare the hash string you fetched from the database against the re-calculation of the hash string based on the user's input when they're trying to log in.

encrypted password in mysql tables, VBA as a front-end

I am trying to add some security at MS ACCESS 2013 applications (using a Mysql BE in the backend).
The first application form will ask for user and password, compare it against a mysql custom table ("USUARIOS", for instance) and grant access if they match or don't elsewhere
But I would like passwords to be stored "encrypted", using AES_ENCRYPT or other convenient algorithm
I have input the user/password directly from mysql-workbench
INSERT INTO USUARIOS COLUMN (ALIAS, PWD)
VALUES ('luis', AES_ENCRYPT('miguel','yucg39dy(9&%$^?bcGSFD'))
but I am failing to retrieve the password to compare (from code, using VBA, loading ADO recordsets).
rs.open "SELECT AES_DECRYPT(PWD, 'yucg39dy(9&%$^?bcGSFD' FROM USUARIOS WHERE ..'
this rs doesn't return the word 'miguel' as intended
I would appreciate any hint on that. Maybe I can use password() or MD5() functions, don't need any key
I have input the user/password directly from mysql-workbench
INSERT INTO USUARIOS COLUMN (ALIAS, PWD) VALUES ('luis',
AES_ENCRYPT('miguel','yucg39dy(9&%$^?bcGSFD'))
rs.open "SELECT AES_DECRYPT(PWD, 'yucg39dy(9&%$^?bcGSFD' FROM USUARIOS WHERE ..'
It's long time ago but maybe try this:
SELECT AES_DECRYPT(PWD, 'yucg39dy(9&%$^?bcGSFD') AS PWD FROM USUARIOS WHERE ..' ('User' = 'miguel';)
All the best
You don't need to retrieve the password.
You should be doing
SELECT COUNT(*) FROM USUARIOS WHERE USER = ? AND PWD = AES_ENCRYPT(...)
and seeing whether the count was 1 or 0.
It will then become clear to you that you don't need to decrypt the passwords at all, so you can stop that insecure plan and hash them instead.

Postfix + MySQL ENCRYPT(), How does it verify the password with randomizing salt?

I've implemented my mail server as dictated here.
It works perfectly fine. My curiousity revolves around entering users into the database and authenticating them
Running:
INSERT INTO users (email, password) VALUES ('sales#example.com', ENCRYPT('password'));
Multiple times will give a different hash for the encrypted password as its utilizing a random salt. I.e. If I enter sales#example.com three times with the same password each hash is different...
My question to this is, how is it that the Postfix server can actually authenticate the password when a user logs in via a mail client?
There isn't any problem per say as it works fine, more just to satisfy my curiosity so I can fully understand whats going on behind the scenes to properly authenticate the encrypted password.
Postfix compares the password from the database to a new encrypt done with the salt(password from db).
to encrypt:
update user set password = ENCRYPT('1234') where id = 1
to check password:
SELECT u.* FROM user u where u.email ='admin#dominio.com'
and ENCRYPT('1234', u.password) = u.password
Read man crypt: it returns the salt in the first two chars of the return value.
So the salt is not lost, you can compare the encrypted string to the result of crypt( 'pass', $first_two_chars_of_encrypted_value ).
You must use ENCRYPT('pass','salt') to force a salt, otherwise the salt is lost forever and you have no way of recovering it. Fairly pointless function without it. It's a terrible function to use, though, because the security is so minimal; use PASSWORD() or OLD_PASSWORD() instead.
ENCRYPT() uses the system crypt(), which may use all or only the first 8 characters, must be printable 7-bit ascii, generally uses 1 round of a DES-based hash, and is completely unportable. Avoid it.

I need helping creating a query to salt and md5 a password then confirm the login on a MySQL Table

I've created a view of a table on a MySQL database to enable another application to use our existing (centralized) clients table.
Our passwords are stored as
md5(password + salt) + ":" + salt
Normally I decode this via a programming language of the given app we're connecting to it but...
This time it's a third party app and I only have SQL and one query to authorize a user.
Can you help me create a valid SQL query to authenticate?
The logic is straight forward:
Get salt for the given user, (everything after the colon)
combine the password and the salt
MD5 the password and salt
then compare the resulted md5 hash
the default sql query for this app is
select * from users
where userName=? and userPass=?
Thanks in advance.
I tried this and it works:
SELECT * FROM users
WHERE userPass = CONCAT(
MD5(CONCAT('xyzzy', (#salt := SUBSTRING_INDEX(userPass, ':', -1)))),
':', #salt)
AND userName = 'bkarwin';
I know you probably don't have the freedom to change anything, but FWIW, MD5() is not considered strong enough encryption for passwords. It's recommended to use SHA-256, which is available through the SHA2() function in MySQL 6.0.5.
select * from users where userName='MyUser' and userPass=concat(md5('MyPass'), ':', salt)
where salt=(
select substring(userPass FROM (instr(userPass, ':')+1)) from users
where userName='MyUser'
)