[ad]
This was the idea with which I have won the regional web apps contest… well actually I did a CMS but the security part of it was the most appreciated. Maybe because it was weird, you’ll see…
Classical Login scripts
What exactly do classical login scripts do… they get the password from the database by querying it with the username (SQL Injection possibility) and after that they compare the retrieved password with the one submitted by the user. If they match either the website sets a cookie, or a variable in the current session…
Weird/Reverse Login script
The main thing that I wanted to achieve was to get rid of any SQL Injection vulnerability. How did I do this? I didn’t use the classical query username in database and get the respective password; instead I searched all the usernames that had the password sent by the current user and then scanned through the list for the username, if not found no such username exists…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
$passwd = $_REQUEST["passwd"]; $uname = $_REQUEST["username"]; $ok = 0; $handle = mysql_connect("", "", ""); $build = "SELECT uname FROM usr WHERE passwd='" .md5($passwd). "'; $query = mysql_query($build, $handle); while($fetch=mysql_fetch_array($query)) { if($fetch[0]!=$uname) { if($ok==1) { //do nothing } else { $ok=0; } } else { $ok=1; } } if($ok==0) { header("Location: somewhere"); } else { //set a weird cookie } |
As far as I see through this method there is no SQL Injection possibility, no need of mysql_real_escape_string() or to worry about hex encoded strings, etc.
Weird/Obscure Cookie
The login process isn’t complete, not until we do not set a normal cookie with “strange” information in it, or should we say obscure information for everybody except the webmaster =).
1 2 |
$build = $REMOTE_ADDR. "secretK3y"; setCookie("cookIT", md5($build), 0); |
As for the secret key…. it has to be secret because if it is not, a person on the same network as yours could forge a cookie to gain access.
Cookie verifier
This is used to check the authenticity of the cookie, I bet you already have an idea on how it looks:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
if(!isset($_COOKIE["cookIT"])) { header("Location: somewhere"); } else { $value = $_COOKIE["cookIT"]; $build = $REMOTE_ADDR. "secretK3y"; if($value!=md5($build)) { //or a fake cookie or changed proxy } else { ...do stuff for users... } } |
Epilogue
It’s not a great thing, could have used sessions or the classical login method with many filters (addslashes(), mysql_real_escape_string())… but I didn’t, it was perfect for me because I am a fan of the principle: “security through obscurity” and also got more points because they wanted creativity… in everything design/development. And because I’m not a designer I had to use my creativity on development. Some of you maybe will like it, others will see it as plain stupidity, and the rest of you won’t even care… but still, it helped me won the contest…
madmax says
Wow mate this is really nice!!
I’ve always thought that the classical method was the only method and for preventing SQL Injection possibility we would have to use filters like mysql_real_escape_string().
This is a really nice and new and most importantly SECURE method
Hats off to U!!
depi says
Nice method, congrats!
Kris says
YOINK!
But seriously, that is a pretty neat idea you had there, well done with winning the contest!
I try to make hard-to-crack login scripts as well, but this has to be the best way i’ve seen.
Reminds me that i need to get around to finishing that boring ASP project i had for college…
Daniel says
Erm.. what if you had lots of users say 10,000 (not really many but enough to quantify my point)? You would have to retrieve 10,000 records where a WHERE username = ‘user’ would only retrieve one, it’s a waste of resources.
SQL injection is something solved very simply! All you have to do is apply mysql_real_escape_string to the username and there is no way a user can use SQL injection. I would recommend reading up on SQL injection before spreading FUD like this.
If you was using a database abstraction class, it could do this for you so you would never have to worry about SQL injection again.
Rob says
‘ … it was perfect for me because I am a fan of the principle: “security through obscurity” ‘
Please do not get a job in security.
Simson says
“security through obscurity” is not a good thing.
You need to study more…
Gareth Heyes says
Can anyone see the major problem here? If you check the entire database for a password then you can scan all passwords to see if it works. Increasing brute force attacks success rate.
I hope this was just an experiement and not intended for real world usage, anyone reading this should understand that it would create a security hole on a web site.
Sandeep Nain says
Mate,
Although, the trick you used to is something different and unique but is not worth to be used in big applications having thousands of users. Not just security, But even if you look at it from query performance perspective, Its not efficient. Nobody wanna make password field indexed.
and PLEASE DON’T TRUST SECURITY THROUGH OBSCURITY…
Clif says
As some of the other posters said, flipping through all user instances where passwords are equal is a bad idea. You shouldn’t see any issues on a small site, but this won’t scale well at all. Just consider the massive number of users who use passwords like “password”, “abc123”, etc…
If you really want ‘obscurity’ (read hashing), hash both the user name AND the password. Either combine them with or have them in separate fields. If you choose separate fields, you’ll never get more than one result (IF user names are unique!). You’ll have to add some extra logic if you combine them though. md5(“foo” & “bar”) == md5(“fo” & “obar”)
backbone says
As you all said it it may be a resource consuming script on big websites where we have many users, but as I am not going to implement it in distributed web apps you should not worry… If anyone who has read this is going to implement this after seeing your comments (which I thank) I don’t think they are going to use them on large community websites…
Daniel
mysql_real_escape_string can be bypassed with hexadecimal encoding… so this is more secure… I would recommend reading up on bypassing before spreading FUD like this.
Clif
I used password strength enforcing in the CMS. So when you registered, you had to use a password which contains at least two lowercase letters, two uppercase letters, and two number… also the minimum length would be of 6 letters…
Mitchell says
As predicted, I’m one of the peeps out there who aren’t a fan of this script, thought you get an A for creativity.
That aside, this won’t work correctly in the case of two or more people having the same password. In the first iteration of the while loop, it checks to see if the username is the one that was passed in. If not it relocates (theoretically) back to the login screen with an error message. So if 3 people had the same password, and the first result was not the person logging in, then they will never be able to login.
while($fetch=mysql_fetch_array($query)) {
if($fetch[0]!=$uname) {
header(“Location: somewhere”);
}
else {
…set a weird cookie…
}
}
backbone says
Thanks Mitchell, haven’t noticed it… anyway I re adapted the script, so now it should work correctly… I’m happy they didn’t realize this at the contest, because neither did I…
Mitchell says
This doesnt do anything, and there is a single equal sign as opposed to a double:
if($ok=1) {
//do nothing
}
else {
//nothing because $ok is allready 0
}
backbone says
that’s how my logic works…. why do you think it’s a weird script? ;) …anyway I’ve put your double equals, don’t blame me if I use to write single equals…
UndiFineD says
He man, that was a great idea.
Unfortunatly there was a little flaw when I wrote it like this 2 years ago.
Some employees had the same password.
That was a sad day for me :(
I solved it by comparing an array of userids in a loop.
until the userid was found or the array was empty.
set your $ok=0; in the loop, this helps against
login.php?$ok=1
stay creative :)
backbone says
10x UndiFineD added that to the loop… as I knew just really old,old,old while(1) { write(‘old’); } versions of PHP where vulnerable to this…
UndiFineD says
Hmmm,
I’ve been out of the lamp loop for a while…
moved on to data centre management.
oh and my login script was for an intranet environment,
just to be clear on the query mess.
Still, I think this is an ok solution, for some situations.
And creativety should be sponsored and welcomed with open arms.
There aren’t enough developers and designers and some them lack proper guidans thru proper management and training.
bl says
I agree with those that think this is a bit overkill and think that there are better solutions.
1. Loop through the results like the person said above.
2. Use mysql_real_escape_string() Like someone else said
3. or, don’t allow anything except for word chars in their username, and use preg_replace(‘/[^\w]/’, ”, $usr);
That will strip any non-word chars out thus sanitizing it on the way in, which is a technique used time and time again.
I admit though, it is pretty clever.
Sank says
I don’t understand why you go through the trouble of looping instead of just letting the database do it for you?
$build = "SELECT * FROM usr WHERE uname='".$uname."' AND passwd='" .md5($passwd). "';
If zero rows returned, done. Else 1 row returned, done.
Sandeep nain says
Hi sank
Yes you are right he could have used the method you suggested (and most of the developers use it the same way) but it opens up the chances of sql injection..
where as the script written by backbone reduces that risk but on the other hand this script lacks performace… As this script gonna give a shocking performance on a table with 1000s of users….
irony is …. he got a prize for this script… but still its a good try from his side
Sank says
As long as you remove quotes and slashes and whatnot, you should be able to prevent the injection.
Sandeep nain says
Yes you are right and thats what Backbone has tried to explain above.
he clearly mentions that in the script he has given you need not to do that…
Backbone said:
As far as I see through this method there is no SQL Injection possibility, no need of mysql_real_escape_string() or to worry about hex encoded strings, etc
and he is trying to implement security through obscurity…