I'm having a dilemma. I have a field hashedX that is a hashed/salted value and the salt is saved in the same row in the mysql database as is common practice.
hashedX saltX
------ ------
hashed1 ssai3
hashed2 woddp
hashed3 92ofu
When I receive inputX, I need to know if it matches any of the values in hashedX such as hashed1 hashed2 or hashed3. So typically I would take my input, hash/salt it, and compare it to the values of hashedX. Pseudo code:
$hashed_input = hash ($input with $salt );
select * from tablename where $hashed_input is hashedX
The problem is I don't know which saltX I need to even get to the $hashed_input before I can do any select.
I could go through the database rows, one by one, try that salt on my input, then check if the input as hashed/salted with this salt matches hashedX of that same row. If I have a 100,000 records, my guess is that this would be painfully slow. I have no idea how slow since I'm not that great at databases.
Is there a better way to do this, than selecting all rows, looping through them, using that row's salt to hash input, then comparing again to the hashed value in the db?
If it is possible (depends on your hash formula) define a MySQL User Defined Function database side for the hash formula (see CREATE FUNCTION). This way you will be able to get your results in one simple request:
SELECT hashedX, saltX FROM tablename WHERE UDFhash(input, saltX) = hashedX ;
You don't specify which hash algorithm you're using in PHP. MySQL supports MD5 and SHA1 hash algorithms as builtin functions:
SELECT ...
FROM tablename
WHERE SHA1(CONCAT(?, saltX)) = hashedX;
SHA2 algorithms are supported in MySQL 5.5, but this is only available in pre-beta release at this time. See http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html for releases.
Is there a better way to do this, than selecting all rows, looping
through them, using that row's salt to
hash input, then comparing again to
the hashed value in the db?
Yes. A much better way.
Typically a salt is only used to prevent exactly what you are trying to do. So either you don't want to use a salt, or you don't want to do this kind of lookup.
If you are checking an entered password against a given user account or object, you should reference the object on the same line that you have the salt and hashed salt+password. Require the account name / object to be referenced when the password is given, then look up the row corresponding to that account name and object and compare the password against that salt + hash.
If you are keeping a record of items that you've seen before, then you should just go with a hash, (or a bloom filter) and forget the salt, because it doesn't buy you anything.
If you're doing something new / creative, please describe what it is.
Related
Hi I'm Integrating memcache in my codeigniter application,
My query change on the user selected values. some thing like this
$sql='select * from user where user_name="'.$name.'" and location='".$location."'";
$result = $this->memcached_library->get(md5($sql));
if(!$result ){
/* execute query and get $result */
$this->memcached_library->add(md5($sql), $result );
}else{
return $result;
}
This is my approach to handle the keys for each different query. But I have heard some where that md5() is not always unique.?
1> Is md5() always unique?
2> if md5() is not unique than what shoud be the other option....
3> what about crc32() is that unique??
Thanks....
A. Your SQL is wrong it should be
$sql = "select * from user where user_name = '{$name}' and location = '{$location}'";
B. You code might have errors since MD5 is case sensitive
See
var_dump(md5("A"),md5("a"));
Output
string '7fc56270e7a70fa81a5935b72eacbe29' (length=32)
string '0cc175b9c0f1b6a831c399e269772661' (length=32)
Better Approach us using strtolower
$result = $this->memcached_library->get(md5(strtolower($sql)));
C, Is MD5 unique
MD5 cannot guarantee total uniqueness, however there are approximately 3.402823669209387e+38 different values in a 32 digit hex value (16^32). That means that, assuming the math behind the algorithm gives a good distribution, your odds are phenomenally small that there will be a duplicate.
D. Better option is using sha1
$betterKey = "user" . sha1(strtolower($sql));
^- identify request for each table
MD5 is unique enough in your case.
But:
I used this technic for a while before dropping it. Why?
md5 is considered to be too fast for password hashing. But it is still a hashing function and when called each time you have to make a DB query, it might slow down your code. I've seen in the past that hashing the SQL query to generate a key was responsible 20% of the PHP execution time, which was huge.
If you need to delete or update a specific key, for example in your case if a user want to change his location and you want this change to be reflected as soon as possible, you'll have to rebuild the same DB query, hash it, to retrieve your key.
The solution I prefer:
Create simple, short, useful keys instead. Typically, use the method name from your model. If the method in your example is User::getUser($name, $location), make your key:
$key = "User::getUser($name, $location)"
You won't need hashing at all, it will be clearer, and easier to manage keys.
You are refering to the Collision vulnerabilities of MD5. For practical, simple use you can forget about those. So MD5( uniqid() ) is unique. See http://php.net/manual/en/function.uniqid.php
For generating hashes in use like SessionID, MD5() is fine.
I am making a search feature where the search value should be compared with two columns in a db table. One column is just a name and the other column is en encrypted value in format "xxxxxx-xxxx" (only numbers). The user should be able to just search for part of the total string in the table.
For the name comparison I use where name LIKE %search_value%, but for the encrypted value I can't use that way of doing it.
Any ideas to how a good way of doing the comparison would be?
You couldn't use a wildcard search for crypted values, because the crypting of 'a' is ENTIRELY and UTTERLY different than the crypting of 'bac'. There's no practical method of doing sub-string matching within a crypted field. However, a simple direct equality test is doable. If you're a DB-side function like mysql's aes_encrypt(), then you could do
... WHERE
(name LIKE '%search%') OR
(cryptedfield = AES_ENCRYPT('search', 'key'))
For substring matching, you'd have to decrypt the field first:
... WHERE
(name LIKE '%search%') OR
(AES_DECRYPT(cryptedfield, 'key') LIKE '%search%')
basically, if it needs to be encrypted, no part of the system should be able to search it. if it should be searchable, then it probably doesn't need to be encrypted.
otherwise you are kind of defeating the purpose of encryption.
If you're looking for full-text search capability of encrypted data (without the database server being able to decrypt the messages), you're in academic research territory.
However, if you only need a limited subset of searching capabilities on encrypted data, you can use blind indexes constructed from the plaintext which can be used in SELECT queries.
So instead of:
SELECT *
FROM humans
WHERE name LIKE '%search_value%';
You could do this:
SELECT h.*
FROM humans h
JOIN humans_blind_indexes hb ON hb.human_id = h.id
WHERE hb.first_initial_last_name = $1 OR hb.first_name = $2 OR hb.last_name = $3
And then pass it three truncated hash function outputs, and you'll get your database records with matching ciphertexts.
This isn't just a theoretical remark, you can actually do this today with this open source library.
user id or username which is better to save in session and also in mysql which is the best way.
--> "select * from usertable where loginname=" . $_SESSION['username']; or
--> "select * from usertable where id=" . $_SESSION['id'];
whether integer comparison or string comparison which is the best comparison in SQL.
You should usually save the ID, because if you are running a fully normalised database this is the value that is most useful to you when performing other lookups.
What you are thinking doesnt really make much sense to me, really!
Integer comparison or String comparison -- Why do you think one will be faster that the other? Of course, if you go instruction by instruction Integer comparison will be faster, but why do you want that?
My answer: Stick to what ever you want. Choose the one that will be guaranteed to be unique. Plus, choosing username, isnt something great. Generate a unique ID for every user that is logged on. And store that ID into the $_SESSION[..]. Obviously, you would need to have some mechanism to validate the ID, and associate it with the username.
Integers comparison is always more accurate than strings, also you would like to consider the fact, the id is unique for each user, and there maybe an several user with the same username, that depends on your system.
I believe id comparison is the best way to go
I don't know how to ask this so I'll start with what I have already:
mysql_query("UPDATE `questions` SET `votes` = `votes` + 1
WHERE `questionID`='".md5($_GET['q'])."'");
What I want to do is update the row where the md5 hashed version of questionID = some string. Can I do this using MySQL's md5 function?
Edit:
Would it be something like this then:
"WHERE MD5(`questionID`)='".md5($_GET['q'])."'"
Of course. MySQL's MD5 behavior is identical to PHP's MD5.
'WHERE MD5(field)="'.md5($field).'"'
Your method of passing "q" via $_GET isn't secure though. Makes no sense hashing it when the fact that ?q= is visible to users on the browser's address bar. You may want to preprocess "q" thru a Javascript MD5 function first before form submission:
http://pajhome.org.uk/crypt/md5/
If you really want to use an MD5 hash in this way I would suggest precomputing the hashed value of questionID and storing it in a column of the questions table. Index this column. If you used an indexed column in your WHERE clause MySQL merely looks at the index to find the matching rows.
If the column is not indexed, or you use a function such as MD5(), MySQL has to look at every single row in that database table to return the information you are looking for. You will see a major performance boost with the indexed column, especially on larger datasets.
You may also wish to use a salt so that your MD5 hashes are not easily converted to their original value. See this URL for more information:
http://skfox.com/2007/12/18/md5-hashes-and-salt/
You could, but in that code you're using PHP's md5.
EDIT: If you want to search for a row where the hash of the questionID column equals the hash of $_GET['q'], the second looks right. It's not clear what you're using MD5 for, though. Maybe you should give some background.
EDIT 2: Since q is already hashed, it should be (with escaping):
"WHERE MD5(`questionID`)='" . mysql_real_escape_string($_GET['q']) . "'"
Let's say I have a SQL statement like this that checks a user login:
SELECT * FROM users
WHERE username='test#example.com', password='abc123', expire_date>=NOW();
Is there a way in SQL to determine specifically which WHERE conditions fail, without having to separate each condition into its own query and test individually?
In this specific example it would allow the developer to tell users exactly the reason why their attempt to login failed.
For my purposes I'm using PHP/MySQL.
Well, one thing you could do is change your query so it only matches on the username. Then in the code you check the password and the expiration date, returning appropriate errors.
Also, I hope your example query is a simplification; Certainly you should be salting/encrypting/hashing your passwords, and you should include something that limits the number of failed attempts within a certain timeframe, etc...
As far as your actual question (as opposed to the results you are looking for), there isn't a way to get that information from the where clause. The closest you could do would be something like:
SELECT *,
CASE WHEN Password = 'asdf' THEN 1 ELSE 0 END AS IsPasswordMatch,
CASE WHEN Expiration >= NOW() THEN 1 ELSE 0 END AS IsActiveAccount
FROM Users
WHERE Username = 'user'
In MySQL you can put boolean expressions in the select-list. Boolean expressions evaluate to the integer 1 when true, or the integer 0 when false.
SELECT password = 'abc123' AS is_authenticated,
expire_date >= NOW() AS is_not_expired
FROM users
WHERE username='test#example.com';
note: If you need to write a query that works on other brands of RDBMS, keep in mind this use of boolean expressions is nonstandard. Use the CASE syntax that other folks have posted.
PS: This is a tangent from your question, but I urge you not to store passwords in plaintext. Store a hash digest of the salted password. See How does password salt help against a rainbow table attack?
No, the where-clause is applied as a block, and various techniques are used so that not all rows have to be scanned. Also, how would you know which row was the one that was desired?
Additionally, you probably don't want to tell the user too much about why a login attempt failed. Saying too much allows for exploits such as account mining and password attacks.
edit If you truly do want to display this to your user, then split your logic into different parts:
Validate identity
Action: Fetch the corresponding user row from the database
Result:
If no such row exist => invalid account
If row is returned, continue to step 2.
Validate credential
Action: Check the stored credential (password, hash of password or encrypted password) against the supplied password treated in the same way the credential is stored.
Result:
No match => Invalid password / credential
Match => Successful login attempt
Login user
Action: Add data to session etc.
You probably just need to separate the parts of the where clause with 'AND'
SELECT * FROM users
WHERE username='test#example.com'
And password='abc123'
And expire_date>=NOW();
Here is what I came up with:
SELECT
IF(mem_username='test#example.com','true','Error: Bad Username') AS mem_username,
IF(mem_password ='abc123','true','Error: Bad Password') AS mem_password'
FROM MEMBERS
WHERE mem_username='test#example.com' AND mem_password='abc123'
This way I can detect in code for error messages and then display them as necessary.
NOTE: To all of you citing security concerns with the example code, thank you for your concern. However, this is not real production code this was simply a simple example to demonstrate the question I had. It is quite obvious that you shouldn't store passwords in clear text, and that you shouldn't give users specifics on why login fails.