Let's talk security

I think the point is, you don’t store the salt anywhere, so that the hacker would have to guess it.

Example of a random/yet reproducible salt: Part of the Username, Part of another required field for registration = salt.

Granted you need to make sure those fields can’t be updated without updating the password (or verifying the current password so you can re-hash their existing password)

Right, so this would be just about the same as a random salt stored in the same record. However I’m assuming that the hacker has root and has viewing rights to the PHP file and could potentially read what the salt is or where its taken from.

If it’s being computed on the fly, then it’s not a rainbow table. “Rainbow table” is just a fancy name for a table of pre-computed hashes. And a pre-computed table that has to be computed on the fly is not a pre-computed table at all. :wink:

Right, pre computed hashes. So if i know the salt for the entire db, I rehash my dictionary with that salt. How long will that take? One could write a pretty simple application that takes the salt from each record, re hashes the dictionary for that single record, checks for a match, moves on to the next.

Does anyone know how large dictionaries usually are and how long it might take to rehash the entire db once through on a decent machine?

How long it would take depends on the hashing algorithm used. An MD5 hash could have the table for a single salt built in a relatively short time whereas with current computing power a top end hash would take trillions of years per salt/pepper.

If you were going to calculate the all the values at the time of the attack then a brute force approach would be quicker because it would be able to stop when it found a value that worked. A brute force approach is not affected by the application of any salt or pepper as it just runs through values until it finds one that works.

Also if you have access to the server you can already see all the data other than the original value of the password and so you don’t need to work out the password to access someone’s data on this site. The only reason you’d be trying to work out their password is in the hope of being able to use it in some other site to access their data there if they were stupid enough to use the same password in both places and if the first value found that works as a password on this site turns out to be their real password and not some other string that maps to the same hash.

If the DB has only a static salt, then here’s what an attacker would probably do: hash(Salt + common password such as “abc123”), then search the database for that hash. If any user in your entire DB used that password, then in one fell swoop, the attacker would have identified the password for one or maybe multiple users. But if every user has a unique salt, then the attacker can’t check all passwords at once. He would need to re-compute the hash for each user.

How much time it would take depends on the objective and password strength. If the attacker is looking for any random user with a weak password so that he can exploit that user on other sites, then it probably won’t take much time. Let’s say he has a list of 5 thousand most common passwords, 1 million users in your DB, and 1 ms to compute a hash, then it would take about 2 months to identify the users with weak passwords. (Though, password strengthening can increase that compute time by 100x or 1000x.) On the other hand, if the objective were to attack your site specifically by brute-forcing the admin password (hopefully a strong password of at least 8 mixed case, numbers and symbols) then it would take a couple hundred thousand years.

Assuming the hacker has read access to your source code since he rooted you. He can found out where your salt is being applied before hashing:

…Actually… After starting to write out a loop I realized this can probably be done with one query once syntax was worked out.


select
u.user_name, u.hashed_password, u.salt, h.common_pass
from
common_passes h
inner join users u on md5(h.common_pass||u.salt) = u.hashed_password

Not sure if you can access u.salt in this way, but I’m positive there’s some way of doing it, perhaps with some sub queries, or a WITH clause. (I think I hear @r937 ; coming)

I’m not at all suggesting that you try to hide the salt. If the attacker has access to both your DB and your code, then it wouldn’t do any good anyway.

I have an idea for increasing password security and I wonder what you think about it as I’ve never seen anyone mention anything like it. Basically, I would not store the salt either in the application code nor in the database so the intruder would not find it there. The salt would be stored on a separate remote server, most probably in a different location, and the main site would contact (for example a php page through https) the remote server for the user’s salt each time it is required during authentication. Therefore the salt is not stored anywhere except in PHP memory for a split second of the script duration. Obviously, the intruder would see the PHP code needed to retrieve the salt and he would know the remote URL so he could run it himself but the remote code would check the requester’s IP and user agent and if it doesn’t match the one specific to the main server then a wrong salt would be sent back without any warning to the intruder, however the remote script would immediately notify the site admin of a possible attack attempt.

This would work provided the attacker tries to contact the salt server using his own methods since the IP and user agent will be wrong and so he would get wrong salts. When he has wrong salts then he might waste a lot of time trying to brute force a password unsuccessfully. But what if he uses the attacked server to request the salt? I have come up with a second line of defense:

Each time a user tries to log in their login attempt is logged in the database of the main site. Each time the main site requests a salt from the remote server the remote server stores this request in its own database. So as you can see there are 2 databases with separate logs but these two logs have to match. One login attempt means one request for a salt therefore if the remote database contains salt requests that haven’t been logged in the main database then it means someone is trying to break the system. A cron job could be run by the remote server (or any other off-site server) that would compare the logs every couple of minutes and notify the admin of any suspect activity. If the admin can act fairly quickly he may then take appropriate steps to secure his system before any serious damage is done.

Of course there’s the question “what happens in the intruder gets the remote salts in the same way as the original authentication script with storing login attempts in the main database?”. I think it would be very unlikely because first he would need to know that storing login logs is essential not to be caught red handed - how would he know that if the hidden detector is not on this server at all? He would need to break to the other server as well but he may not even think he needs to. Second, an intruder most probably doesn’t want to leave any trace of his activities so he would certainly craft his script to fetch the salts without storing his actions in the log.

This solution would certainly require a separate secure and reliable server for keeping and serving salts but in my opinion it would make his job a lot more harder compared to a situation where all passwords and salts are available after breaking into the main server. The main help would be in having a mechanizm that notifies the admin that someone is trying to get to the passwords. What do you think about it? This is entirely my own idea and I wonder if it is any good:)

Seems like we keep moving the salt one more server away. First the salt was in the database. But what if the database server is rooted? Move the salt to the application. But what if the application server is rooted? Move the salt to some other remote server. But what if…

None of these solutions have fundamentally fixed the problem, because the problem is: “What if your servers are compromised.” In that kind of scenario, you have to assume that the attacker knows everything and has access to everything.

It is called “security by obscurity” and has nothing to do with security at all - it just makes the person implementing it feel like it is more secure even though it isn’t. It is a really common mistake made by those who know very little about security.

What everyone needs to realise (as you and I already know) is that with an appropriate hashing algorithm you could have a JavaScript version of the hashing where everyone can see all the code and it would still be just as secure - because the hashing algorithm together with salt and pepper do not rely on obscurity at all in order for them to be secure.

If the attacker must break into two servers instead of just one in order to get to the passwords then isn’t it logical that the system is more secure because it is more difficult for the attacker?

Yes, but I think the ultimate goal is to keep everything within the same server. So I’m curious if something like I mentioned earlier is possible.

select u.user_name, u.hashed_password, u.salt, h.common_pass from common_passes h inner join users u on md5(h.common_pass||u.salt) = u.hashed_password

Assuming it is, what steps should be taken to hide how either the salt is implemented, or add in another piece of security.

I think that SQL query hides the fact that there will be thousands of common passwords to be tested against each user – an n^2 operation. Effectively the query is trying to just guess passwords, one by one, and there’s really nothing you can do to stop that except to make it computationally expensive. If you hash your passwords not jut once, but iteratively 100 or 1000 times, then you can significantly increase the time it takes to guess passwords. The 2 months I quoted earlier could become 200 months (~16.5 years).

My example was looking at a rainbow table rather than a brute force attack.

If you have a unique salt for each user, which your SQL query implies, then rainbow tables can’t be used.

I was also supplying an example of how it might be used. I know the syntax is not 100% accurate but I know it can be done, depending on computing power. At worst, I can run a loop to rehash my rainbow directory for each record to check for a match, but I believe it can be done with one simple query (such as the one I posted twice)

This part right here is what makes it not a rainbow table. A rainbow table is a pre-computed table of hashes. But if you have to re-compute that table for each user, then it isn’t a pre-computed table at all.

Ah but that begs the question, if you take an existing rainbow table, and recompute the hash, is it not still a rainbow table? You just rebuilt the hashes, because you saw in the source code that the “pre-existing” rainbow table you had would be insufficient.

Granted, the time it would take to generate new hashes for an existing rainbow table could be lengthy, but in the end if you are going to utilize the new re-computed hashes in the same manner you would have utilized the pre-existing rainbow table, is it not the same?

I’m not going to argue that one way over another, but it does strike me as an interesting consideration…

Technically, yes, it’s a rainbow table, but it’s the most useless rainbow table you’ll ever make, because it’s specific to only one particular salt/user. Basically you’re brute-forcing a password and storing all the incorrect passwords in a table, and those stored results can’t be reused.