How to hash passwords in MySQL? - mysql

How I will make every password in my user table encrypted(md5()) except one particular row using a single query?

UPDATE table SET Password = MD5(Password)
I will say though that MD5 isn't a very good level of encryption and you should consider something stronger such as ENCRYPT with a custom salt. Read about it here
EDIT: Looks like the original question changed. Here's the altered query to accomodate
UPDATE table SET Password = MD5(Password) WHERE ID!=[specified index]
EDIT: Worth noting
MD5 Encryption Hacked

Hash Functions in MySQL
There are a lot more hash functions than MD5 to use for storing passwords in you MySQL database.
You can find a list of them on MySQL :: 11.10.2. Encryption and Compression Functions.
Save Password (hash):
UPDATE users SET password = SHA('secret_password') WHERE ....;
Check Password:
SELECT COUNT(*) FROM users WHERE name = 'username' && password = SHA('typed_password');
If the result is > 0, the user provided the correct password.

When hashing passwords, do not forget to salt them, so that same passwords do not yield same hashes:
SET #salt := CONV(FLOOR(RAND() * 0x100000000), 10, 16)
UPDATE passwords
SET password = CONCAT(#salt, SHA(CONCAT(#salt, #typed_password)))
SELECT 1
FROM passwords
WHERE SHA(CONCAT(SUBSTRING(password, 1, 8), #typed_password)) = SUBSTRING(password, 9, 40)

Concerning you edit: do you have an ID or username that identifies this row?
UPDATE mytable
SET password = MD5(password)
WHERE id <> 123

Edited in response to edit in OP.
UPDATE userTable
SET password = MD5(password)
WHERE NOT (<criteria to identify row to exclude>)

I think it is a little bit more update
SET PASSWORD FOR 'existinguser'#'localhost' = PASSWORD('newpass');
or
UPDATE user SET password = PASSWORD('newpass');
Hope this help

Related

Slow Encryption in Coldfusion

I have been trying to encrypt all the user's password in the MySql database using the following logic:
<cfquery datasource="mydatabase" name="userlist">
select userid, password
from mytable
LIMIT 0, 2000
</cfquery>
<cfoutput query="userlist">
<cfset pwd = encrypt(#userlist.password#, mykey, "AES/CBC/PKCS5Padding", "hex")>
<cfquery datasource="mydatabase">
update members
set password = '#pwd#'
where userid = '#userid#'
</cfquery>
</cfoutput>
This seems simply enough just to encrypt 2000 records. But the CF takes over 2 hours to do this! It averages only encrypting 13 records in one minutes. Is it something wrong with the code or is there a problem in CF Admin setting that needs to be tweak to speed up this thing?
p.s. the CF template does run but it will show 504 time-out on the browser in few minutes. However, the 2000 records will still be completed in the background in 2 hours time.
EDITED:
I ran the first set of query on the database directly. It only took 0.05 seconds to retrieve all 2000 rows. The second query to updated the database with already encrypted password took also less than a second. That leaves only the CFOUTPUT query and the CFSET ENCRYPT lines that are causing the the 2.5 hours run. I don't know how else to optimize this.
Working within comments was getting difficult..
In this post, I don't salt hashes, but it's just example stuff, you should definitely salt (add random, but consistent, string data).
Are you familiar with hashing? There is no need to encrypt passwords in a decryptable format. Instead, you can hash the user's entered password each time they enter it (registration, login, matching for password-change).
Among the several reasons hashing is preferred: Users typically insist on using ONE or as few as possible passwords everywhere. By providing their password in password-recovery (email "Your password is p#55word"), or over the phone, you may expose much of their online existence.
I do not use mysql often but you can md5 hash on the database side, as shown here, MySQL MD5 SELECT. Further, if you salt the hash (add your own string (like "sodium") to the hash that is not exposed anywhere, so long as it's the same salt for the user each time), you greatly increase the security as databases of straight hashes of entire dictionaries exist. Cookies md5's to 597b56e53847cd6a4712ac183f61fa68 but Cookiess (one more s) md5's to the very different 16fb9ddb550f238b848f8490854ef792.
You could use the link I referenced above to hash into a new column and experiment for data integrity for your own ease. Something like
update table
set hPwd = md5(password + 'Sodium' + username)
where hPwd = ''
limit(0, 2000)
so that you can still do it in pages if you like. There exist several hash functions, see dev.mysql.com/doc/refman/5.5/en/encryption-functions.html like SHA1.
Jack replied
Thanks. I would definitely use hash if not for the times that we may need to go into an account as a customer sees it, which means we need to know the password
Let me explain
When a user logs in, you would match their criteria like this
select data_you_want
from users_table
where username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.username#">
and password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#hash(form.password)#">
If that matches, you'd store the hashed password if you want to store the password in session data. Any pages where you want to re-affirm that the logged in user has the correct credentials, you'd use a query like this (notice the lack of hashing this time, vs the login query). Additionally you'd store asAdmin = 0, as a flag indicating that this is a normal user logging into their own account.
select data_you_want
from users_table
where username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.username#">
and password = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.password#">
So, RE: wanting admins to be able to login. You create an admin gateway page that requires a different set of credentials (I'll get back to that) and if successful, you allow the user to login.
Because you want the user to still have some security, you might create a device like this... If a user is asking for admin assistance, you can give them a page to create an admin key like say #randrange(1,1000000)#
<cfset akey = randrange(1,1000000)>
...
update users_table
set akey = <cfqueryparam cfsqltype="cf_sql_varchar" value="#akey#">
where userID = <cfqueryparam cfsqltype="cf_sql_integer" value="#session.userID#">
...
You've requested some help from customer service and want them to login to your account.
Please give them this key #decimalformat(akey)#.
<!--- Decimal formatting makes the key easier to read, it's not stored that way --->
And then, when an admin logs in from a login page within the admin system, they enter the username and THIS KEY rather than a password.
That looks like this
select data_you_want
from users_table
where username = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.username#">
and akey = <cfqueryparam cfsqltype="cf_sql_varchar" value="#form.akey#">
If successful, it stores information similar to the login page that users normally access, no longer worries about the admin key.
Like
<cfscript>
AdminID = session.userID;
structclear(session); // get rid of all admin credentials, we're mimicing the user.
session.asAdmin = AdminID; // Create a flag that we can append to database activity if we want to track whether a user did this or an admin (and which admin!) did this on their behalf. If it's 0, it's the normal user, if it's greater than 0, it signifies which admin user did this.
session.username = GetUserStuffs.username;
session.pwd = GetUserStuffs.password; // because we're pulling a hashed password from the db, we do not rehash it.
session. ...other credentials you want in the session scope, the same things you'd store on normal user login...
</cfscript>
<!--- Now, use a client side redirect to send the admin, now logged in as the user, to the site root, or any normal-member-accessible-page. --->
<script>
window.location = "/";
</script>
You can also reset the akey for the user on this page, so that the key is only good once.
Alternatively you can do this all without an akey but that allows your admins free roam of user accounts and that's grossly insecure.
I think the issue may be because of having lots of individual cfquery calls when you execute the code so it's not very efficient. In the past I've tackled this by just using ColdFusion to create and render the SQL in your browser. You can then take that output and run it directly against the database which should be quicker to execute.
So your code would be something like this:
<cfquery datasource="mydatabase" name="userlist">
select userid, password
from mytable
LIMIT 0, 2000
</cfquery>
<cfoutput>
<pre>
<cfloop query="userlist">
<cfset pwd = encrypt(userlist.password, mykey, "AES/CBC/PKCS5Padding", "hex")>
SET #pwd = '#pwd#';
SET #userid = #userlist.userid#;
UPDATE members
SET password = #pwd
WHERE userid = #userid;
</cfloop>
</pre>
</cfoutput>
This will produce an output something like this:
SET #pwd = '0E8C9C1026C0EB950CA8A398C8E5ED6B';
SET #userid = 1;
UPDATE members
SET password = #pwd
WHERE userid = #userid;
SET #pwd = 'F6B9CE35A39A15D791F7C3711399493A';
SET #userid = 2;
UPDATE members
SET password = #pwd
WHERE userid = #userid;
SET #pwd = '1DB3AA5B3DF622CE905BD4E992B24E6D';
SET #userid = 3;
UPDATE members
SET password = #pwd
WHERE userid = #userid;
You can then run those SQL statements directly against the database server.
Ideally you should be using one-way encryption, but that's another question which is detailed here: https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords

Hashing an entire column using sha512

I have a table with three columns named: Question, Answer, Hashed. I want to update the Hashed column with the Answer column hashed using sha512.
I've tried to do the update directly from my MySql database using this syntax, but it didn't work:
UPDATE TableName
SET Hashed = SHA512(Answer)
WHERE Hashed IS NULL
I know the syntax is wrong but not sure why.
Thanks in advance for your help!
R
Give this a shot.
UPDATE TableName SET Hashed=SHA2(Answer, 512) WHERE Hashed IS NULL;
Note that this will only work with MySQL 5.5 onward. For versions before 5.5, you'll have to use application code to hash it (PHP to get all the rows, iterate through and hash $row['answer'] to SHA512, then run the UPDATE commands on each)
(Source: http://dev.mysql.com/doc/refman/5.5/en//encryption-functions.html#function_sha2)
I hope this is not too late. Even if, maybe someone else will find out this hint:
UPDATE TableName SET Hashed = ENCRYPT('Answer', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))) WHERE Hashed IS NULL;
What it does, it creates sha-512 hash, with it's schema: $6$ from string 'Answer'
If you are using debian, you may also use mkpasswd from package libstring-mkpasswd-perl to generate SHA-512 for you, and update as string.

SELECTing in a WHERE clause, inside a SELECT statement

I'm not exactly sure on the correct technical wording, so excuse my title, but here's the problem. I have a MySQL database, and in the user table I have *user_name*, a *password_salt*, and an md5 password containing the password then salt. In a program, users connect and I get one query to send to validate a user.
When a user connects I need a way of selecting their user_name, and comparing the given password to the stored password, which requires retrieving the salt somewhere in the WHERE statement (I guess).
This is my hypothetical "example":
SELECT user_name
FROM users
WHERE user_name='$nick' AND
password = md5(CONCAT('$md5pass', md5((select password_salt FROM users where user_name='$nick'))))
LIMIT 1
Resolution Update: Got it working, thanks for the suggestions, a normal select sufficed, the problem was that the sql-auth api wasn't receiving the password unless the port was specified.
Actually you can freely use any column from table declared in "FROM" clause not only in "SELECT" clause, but also in "WHERE" clause, so I don't see a need to subquery here. Let it be simply:
SELECT user_name
FROM users
WHERE user_name='$nick' AND
password = md5(CONCAT('$md5pass', md5(password_salt)))
LIMIT 1
This way a row is selected only if it matches both:
- user name is correct
- the password in row matches given password
I am not sure though if I used md5() functions correctly. I copied your example.
SELECT user_name
FROM users
WHERE user_name='$nick' AND
password = md5(CONCAT('$md5pass', password_salt))
LIMIT 1
Try this instead:
SELECT user_name
FROM users
WHERE user_name='$nick' AND
password = md5(CONCAT('$md5pass', md5(password_salt)))
LIMIT 1

Login Server Side Logic

This question has more to do with how I am setting up my server side code for a simple login script. I'm interested in the best way to achieve my goal, which is of course to verify a users username and password against a database and present them with either a successful login, a registration page, or a username or password found, but the alternative is wrong.
Right now, I have it set up where my sql query scans the database for both the user and pass:
SELECT * FROM test WHERE userName='" + userName + "' AND pass='" + password + "'"
Problem with this approach is it either returns a true or false...I cannot tell if one of the inputs was correct and the other wasn't. It either finds the record, or it doesn't.
So I could query based on the username alone, and if found check the record for the correct password before passing the user onto a successful login. That way I know if the password is wrong, but I have no idea if the password is right and the user simply types the wrong username.
Alternatively, I could extend on that, and if the user isn't found, requery the database based on the password and determine if I can find a record but the username doesn't match. It seems like a lot of back and forth with the database, which is fine. But i'd like to hear from some experts on whether or not this is a proper approach.
I have not much idea wether stored procedure is supported in my sql or not. If it is supported then you can make SP like this way to check all cases. Below is code for MSSQL, you can check it with my sql :
IF EXISTS(SELECT [id] FROM [dbo].[users] WHERE [user_name] = #user_name AND [password] = #password)
BEGIN
SELECT 1 AS RETURNVAL --Valid User
END
ELSE IF NOT EXISTS(SELECT [id] FROM [dbo].[users] WHERE [user_name] = #user_name)
BEGIN
SELECT 0 AS RETURNVAL -- User doesn't exist
END
ELSE
BEGIN
SELECT -1 AS RETURNVAL -- Password Not Correct
END
You don't want to disclose too many information to people with bad intents trying to probe your system for available usernames (or even – god forbid – passwords that are in use).
When a login attempt failed, simply display a message stating:
Username and/or password mismatch.
As an aside, use prepared statements, rather than string concatenation when working with your database; it protects you from SQL injection attacks.
Plus – although it's not entirely clear from your code snippet – don't store plain passwords or plain password hashes. Rely on one of the many available and well tested encryption/hashing libraries e.g. PHP's crypt function (make sure you select a proper hashing function such as SHA512).
Your code in the most simplest form would then look like this:
// coming from your login page
$dbh = new PDO(…);
$sth = $dbh->prepare('SELECT `digest` FROM `users` WHERE `name` = :name LIMIT 1');
$sth->prepare(array( ':name' => $_POST['username'] ));
$result = $sth->fetch();
if($result !== FALSE && crypt($_POST['password'], $result['digest']) === $result['digest']) {
printf('You logged in successfully as %s', htmlspecialchars($_POST['username']));
} else {
echo 'Sorry, username and/or password did not match! Please try again.';
sleep(1);
exit;
}

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'
)