I am using vernemq to authorize users from database using SHA256 algorithm. I observe that when creating new entry in table using buildin mqsql function SHA2,
INSERT INTO vmq_auth_acl
(mountpoint, client_id, username,
password, publish_acl, subscribe_acl)
SELECT
'', 'newUser2', 'newUser2', SHA2("CJJPL9", 256),
'[{"pattern":"botOut"}] ',
'[{"pattern":"botIn/#"}]';
which generates hash value something like this 54d0e30d0a00d86451a3353a2123fc1f006faaba6b55ef0d168390f26cbab82a and Vernemq server successfully verifies this user when logged in using CJJPL9 password.
But when I add this entry into this table from python code using passlib library sha256_crypt.encrypt("CJJPL9"), which generates hash as follows
$5$rounds=80000$wnsT7Yr92oJoP28r$cKhJImk5mfuSKV9b3mumNzlbstFUplKtQXXMo4G6Ep5. I know, due to salt, the hashs will be different, but there is a clear difference in the formatting of both hashes and as a result, verneMQ server fails to verify the credentials for this user. The formatting of passlib library says that it keeps only 43 characters from 256-bit checksum. I also tried testing by only keeping the checksum part of the passlib hash string, but still no luck.
I want to know what is the difference between encryption of MySQL SAH2("CJJPL9", 256) and sha256_crypt.encrypt("CJJPL9").
Related
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
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.
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 = ?
I want to authenticate users for a folder from a MySQL 5.1 database with AuthType Basic.
The passwords are stored in plain text (they are not really passwords, so doesn't matter).
The password format for apache however only allows for SHA1, MD5 on Linux systems as described here.
How could I generate the right format with an SQL query ? Seems like apache format is a binary format with a lenght of 20, but the mysql SHA1 function return 40 long.
My SQL query is something like this:
SELECT CONCAT('{SHA}', BASE64_ENCODE(SHA1(access_key))) FROM user_access_keys INNER JOIN users ON user_access_keys.user_id = users.id WHERE name = %s
where base64_encode is a stored function (Mysql 5.1 doesn't have TO_BASE64 yet). This query returns a 61 byte BLOB which is not the same format that apache uses. How could I generate the same format ?
You can suggest other method for this too. The point is that I want to authenticate users from a MySQL5.1 database using plain text as password.
The document you refer to does not mean to imply non-Linux by "Unix-only", therefore crypt() is available to you. Since you seem unconcerned about security I'd suggest this is the easiest option alongside MySQL's encrypt function. You may want to use a constant seed value in your queries.
SELECT ENCRYPT(access_key, 'SA') FROM user_access_keys INNER JOIN users ON user_access_keys.user_id = users.id WHERE name = %s
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.