- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
The real WTF is that so many WTFs are just stupid "SELECT * FROM table" SQL statements. Where the hell is the real WTF code nowadays?
Admin
That depends on what kind of site it is. When a site makes me sign up for something I just want to see one time, I try to make the username and password as easy to guess as possible so people can guess it and harass people on the site.
Admin
(Note: I'm assuming a web interface here)
The problem with that is reliability. You can never rely on the client to actually work properly (or have JS enabled at all), thus you'll end up with legitimate users who can't get in. Tunneling through SSH (https) is really the only worthwhile way to avoid sending plaintext passwords from client to server. With anything else you'll just end up running into security and/or reliability problems.
Admin
You're missing the joke in the subtle wording of what he said:
He's saying tell the user when their password was correct, but not the username. Think about it: if the username is wrong, how can the program possibly know whether or not the password is correct?
Admin
(Sorry, didn't login and can't edit) I was assuming by "client" and "server" you meant web client/server. If you meant SQL client/server then nevermind.
Admin
Pass the towel. I just puked on my keyboard. 1.Inline SQL 2.Select * 3.improper while loop 4.WHERE clause...what WHERE clause? 5.God help the poor bastard if this in a web interface
Admin
Most likely stuck directly in a "Click" event handler.
On an aside, I can't believe the number of people here who clearly have no understanding of what a salt is for. Generating a random hash during authentication does not help prevent a dictionary attack. That's just a half-baked implementation of a security context token, and it doesn't work at all when you have no control over the "client" (which you don't on the web).
Just hash once. Further hashing actually makes you less secure because every hash carries the probability of collisions, and you're increasing that probability with each successive hash. Adding a random salt doesn't help, because anyone who can see the hashes can also see the salt. If you're worried about traffic sniffing, they came up with a solution for that years ago, it's called SSL!
Admin
Try http://www.bugmenot.com first. :)
Admin
Some apps I've seen made by lazy users store passwords in plaintext.
Oh ... that decryptMd5() is possible, thanks to MySQL. I don't know how they implement "md5", but md5(md5('doofus')) will return 'doofus' in MySQL. Kind of defeats the purpose altogether...
Ahh, but The Real WTF(tm) is that they should be using LDAP for authentication. ;)
Admin
Actually, the outer hashing with salt is already covered by HTTP digest authentication. The inner hashing can be performed on the server, which is pretty much equivalent from a security point of view.
Anyhow, you are right about the benefits of SSL. I usually use it because if you need secure authentication, you most likely need the confidentiality and message integrity features offered by SSL as well. What use is a secure bank login if account information and transactions are transmitted in plaintext and subject to normal network eavesdropping and injection attacks?
Nitpicking: SSH is shell over SSL. HTTPS is HTTP over SSL, not HTTP over SSH.
Admin
Admin
would have been sufficient.
Admin
Admin
SecurityUtility.Encrypt is another one of my methods. It handles the salted hash. (mmm, salted hash browns, I'm hungry now).
And, for this particular instance, since, like I said, I'm not too concerned with the data actually being compromised, I'm just fine with the salt being baked so that it's just unique to my site.
Admin
Or, instead of going all the way to stored proc land:
Select Count(*) Where ....
The only advantage I can see for a stored proc is that with that approach you could not allow the server to access the user/password table at all (only allow external access to the stored proc), which would eliminate the possibility of someone stepping in and pretending to be your server. 'Course, if that's a possibility, you have many more serious security issues to deal with in the rest of your app too, and just returning a boolean to the (bogus) web server and expecting them to act honorably with it is beyond mere silliness.
Admin
You've confirmed this?
Admin
You've confirmed this?
Admin
Admin
Admin
And yes, I realize it's not quite impossible. But that's because MD5 is not totally secure.
Admin
The real WTF is a unit test obviously didn't exist for the unit test. Everyone knows you should unit test your unit tests, otherwise how can you be sure your code is correct?
Admin
I think I know "Karl". I used to have a developer working for me who wrote this kind of code (slurp a whole DB table into memory and scan it in his code).
Admin
What good does it do to pass a hash over the network instead of the plain text password? Either way, a snooper has what he need to authenticate. I mean, the server's just going to compare the hash from the client with the hash in the database, right? It is actually worse because getting the users table gets all the "passwords" with no cracking required.
Admin
The real wtf is 120 or so comments about password hashing. The password is already hashed before it gets to this function.
To the people who are commenting about weak passwords, the user register, should not even allow password like the ones you guys have suggested.
The problem wasn't the unit test itself, it was the amount of test data that was tested.
Anyway enough of that crap, a better solution would be:
Admin
//jotted down
Admin
Yup - if you just send, for example, md5(password) in cleartext everywhere, then congratulations - you've just made your system as insecure as plaintext passwords, as the md5 hash has become the password.
If you have a secure comms channel, e.g. SSL/TLS, it's easiest to just send the plaintext password over that secure channel, and use a salted hash on the server end and in the DB.
If you have an insecure channel, something like CRAM-MD5 or similar is a good idea (although this requires the server to have access to the decrypted password - which can be a problem in itself):
Admin
You might want to look into a new line of work if you don't find this code absolutely painful to look at or think about. The person who wrote it was/is an idiot with no clue.
Admin
Oh Dammit. I copy pasted the SQL line from something else and lost the rest of the WHERE clause.
Good catch.
Admin
with a good password(which should be enforced the user management), it is vary vary hard(the only method to crack the password is a brute-force attack) and time consuming to crack the password (which should be change constantly).
There is still a possiblity of a man-in-the-middle attack. More for the CRAM-MD5, but still there is a threat for ssl.
Admin
Nitpicking your nitpick: SSH (the protocol) does not use SSL (the protocol).
Admin
SSL: Second that.
Salt and then hash. That is enough. That is application security, which should be implemented.
If you worried about network security, then you use secure network mechanisms.
If you are worried about client (PC) security: Often, you do not have any control over this. If you have your user accessing your application from non-secured and/or compromised clients, then that's that - case closed: access to your application for the users in question is compromised.
That's bad enough - but the problem can be mitigated depending on the nature of the application: i. e. for e-banking web apps two-factor (or even three-factor authentication) should be implemented (at least) for performing actual transcations.
One way to do this are one-time-passwords (in Germany they are called TAN (short for german Transaktionsnummer - in english transaction number). You get a pad (called a block)in a secure tamperproof envelope sent to you by mail from your bank which contains about one hundred transactions numbers. Any new block (with any transaction number) contained therein) needs to activated by the end user in the banking app using an unused transaction number from the current TAN block. Doing this successfully deactivates the current TAN block and at the same time activates the new TAN block.
Before, you as an end user could select at your will which TAN from the current TAN block to use for a transaction. Nowadays (presumably because users were running out TAN on their current block or writing them down elesewhere/storeing them on PDAs or whatever) the banking apps requests you provide a TAN specified by row and cloumn on the original TAN block paper.
Of course, this is not foolproof: The biggest WTF that you can do with this scheme is to store your banking app login info together with the TAN block. That is why the banking apps in Germany do not require TANs for login authentication.
Admin
Had you read my next comment...
Admin
Actually the REALL issue here is writing code like this in the first place. What experienced coder (even on that sucks) pulls every record in a table when the conditions for the where clause are in his hand? That's a noob error - who put the noob on security?
Admin
In such cases the password that the call center employee is asking you for is your ACCOUNT password, not your LOGIN password, and as such, it must be stored unencrypted so the service rep can verify the account password provided over the phone. (unlike your login password).
Such account passwords are a good idea, otherwise it is too easy for someone to call the company concerned and make unauthorized changes to your account or gather personal information about you.
Generally I do not like to deal with companies that do not use an account password, having had someone obtain a phone in my name without my knowledge once.
That said, plenty of sites do not hash the password before it is sent to the webserver. It can be achieved with javascript, but what if it is not enabled in the clients browser? Login will fail. It is foolish to rely on javascript. The only way to ensure that a password is reliably encrypted before transmission is to use an SSL webserver.
The real WTF here is that so many posters here think they know what they are talking about.
And yes, the author of the code posted is obviously pretty clueless.
Admin
Perhaps the call centre person could type in the password you read to them over the phone? Then the system would do a normal password check, and a plain-text password doesn't need to be stored... Just an idea.
Or even do what HSBC in the UK does -- only ask for particular digits of the password, so it never really goes in one chunk. (Horrible to use, but seems slightly more secure.)
Admin
Two of your points are untrue. Regarding hashes:
Also, SSL is secure against man-in-the-middle attacks. That's what certification authorities are for. The client verifies that the server's certificate was signed by a CA. An attacker can either:
Admin
I hope this guy writes a book. I could use a good anti-pattern non-fiction novel. :)
Admin
What language is the OP coded in? C#? Can strings be compared with == there?
Admin
50% wrong! is not "select " but "select count()" (please, don't use databases like a rubbish bin...)
Admin
Admin
Admin
With reference to comments like this:
In the case of web based authentication, confirming to the user that their username is correct but the password they have entered is wrong is not in itself a security hole, but it is very convenient for end users.If you enforce reasonably strong passwords, giving someone the username will make zero practical difference to their ability to brute force the login information.
It seems worth mentioning at this point you should check for failed login attempts - and act appropriately when brute force attacks are attempted from a specfic location and/or on a specific username.
Caveat: If the username is an email address and if your site is of a "senstive" nature (e.g. a porn site), then this approach would be ill advised.
It should be obvious when it's going to be suitable to do this and when it's not, but to suggest that it's 'never' appropriate and that displaying a user friendly message indicating that the user got their username correct, but the password was incorrect is a significant security risk is just plain wrong (FUD).
If your passwords are not strong enough to make knowing the username effectively a moot point, then you have a far more pressing problem you need to deal with.
Admin
Though, those schemes are still vulnerable to MiM attacks. Something banks in sweden have noticed as of late. Nordea has had a few suck attacks directed at their clients the last few years. Most of them discovered due to crappy frontpages used by the attacker, but one kept going for a week or two I think, before someone computer savvy customer used it and saw what it was.
Admin
You get points for being the first person to bring this up. Adding a where clause to this is NOT a solution, and is in fact another WTF because you are now about to assemble a SQL query from USER PROVIDED input.. That's just begging to be left open to an injection attack. All input is evil, and should be treated as such.
This should all get handled by a parameterized sproc, and there ought to be some input testing making sure that all chars in the username at least are 'allowed' chars..
Admin
Come one, "developer tries to do a table scan instead of a unique select ... from ... where ..." has been done before.
Admin
Come on, "developer tries to do a table scan instead of a unique select ... from ... where ..." has been done before.
Admin
Sadly: yes - it is will not eliminate Man-In-The-Middle-Attacks. On top of the scheme described above SSL is always used, too (at least in Germany).
Security is not about being perfect - after all, what man can lock man can also unlock again ... this age-old adage has been valid throughout the ages and is even more valid nowadays. Security is about lowering the odds for comprimising the secured system/items/whatever for an attacker:
If an attacker has to go through too much trouble (i. e. use too much time/ paying too much money etc.) negating the benefit he will gain by compromising the targeted system/items then he will abstain from compromising the targeted system/items.
The schemes described above are in full production use in the banking industry in Germany (and problably a number of other european countries, as well - although I can not comfirm that). Together with SSL, IMHO they raise the bar significantly compared to a simple non-SSL username/password as it is employed with some US banks (I recall some news items during the last month on network-world.com on this subject).
Back to the scheme described above: I omitted that you never have to validate your login with a TAN. And the banking get extensively briefed about that (and about e-mails purporting to be from your bank) ... still there are some people who willingly click on a link in a phishing e-mail and login on a phishing web site with user/account-id, password, additional login info as required by the real banking site and a TAN ... in my opinion: this is 2007 and how stupid can you be not to have heard the shot(s) by now ... just proves to me that there always will be fools and the people who will take advantage of them.
Philosophical: I wonder if this is basic human nature, or what !?!
Admin
I suppose you only run your tests against a live data base using real customer data?
Admin
The code doesn't even validate under unit testing except in the single case that the username/password being tested was the last record returned in the data set. The repetition structure sets this.authenticated after each iteration based on the value returned in that iteration. So the first incorrect match after the correct match would reset this.authenticated to false even though the function should have resulted in an assignment of truth. Of course proper use of the WHERE clause would make the whole thing much more efficient.
The original code: public void Authenticate( string username, string passhash ) { SqlDataReader source = _Database.Query("SELECT * FROM users;");
}
A better implementation: public void Authenticate( string username, string passhash ) { SqlDataReader source = _Database.Query("SELECT * FROM users;"); this.authenticated = false; while ( source.Read() ) { if ( source["user"].ToString() == username && source["pass"].ToString() == passhash ) { this.authenticated = true; break; } } }
Admin
Nice. The Oracle equivalent is:
SELECT 1 FROM whatever WHERE your_criteria=something AND ROWNUM = 1;