Hi,
I wanted to post my login system I will use for an upcoming site for rating. I want to ensure a safe login, so please, if you know anything about this and see a security leak somewhere... Please post, any remarks are welcome.

How it works: the script generates a random number if the form hasn't been submitted yet. This number is being passed to the Javascript also. On submitting of the form the javascript creates a hash and empties the password field. The hash includes: IP + hashed password + random number. The server recreates this and destroys the session with the random number *. The two hashes are compared and a decision is made.
* I've been thinking of putting the random number in the database and an id in the session, then the random number is pulled of the database. However, I'm not so experienced in login systems and don't know what information can be corrupted.

NOTE: the script will also log who is currently logged in, I still have to code that part, but I couldn't wait to upload this here.
EDIT: I will include a script that will only allow for so much login attempts in a certain time span to exclude bots. Or shouldn't I? :)

<?php
	session_start();
	include_once("connect.php");
	
	if(isset($_POST['logIn'])) {
		$RND = $_SESSION['RND'];
		session_destroy();
		$IP = $_SERVER['REMOTE_ADDR'];
		
		$qGetUser = @mysql_query("SELECT * FROM users WHERE gebruikersnaam='".$_POST['username']."'");
		if(@mysql_num_rows($qGetUser) == 1) {
			$aGetUser = @mysql_fetch_assoc($qGetUser);
			$serverHash = sha1(($IP.$aGetUser['wachtwoord'].$RND));
			
			if($serverHash == $_POST['hash']) {
				$msg = "NICE!";
				$type = "notification";
			}
			else {
				$msg = "fail :( serverHash: ".$serverHash." ; clientHash: ".$_POST['hash']." ; wachtw: ".$aGetUser['wachtwoord'];
				$type = "error";
			}
		}
		else {
			$msg = "De ingevoerde gebruikersnaam is ongeldig.";
			$type = "error";
		}
	}
	else {
		$_SESSION['RND'] = getRandomNumber();
	}
	
	function getRandomNumber() {
		srand(time());
		return (rand()%1000001);
	}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
		<title>Vermeersch Constructie</title>
		<script type="text/javascript" src="MooTools_Functions.js"></script>
		<script type="text/javascript" src="MooTools_BackEnd.js"></script>
		<!--[if lt IE 7.]>
			<script defer type="text/javascript" src="pngfix.js"></script>
		<![endif]-->
		<link rel="stylesheet" href="style.css" type="text/css">
		<script type="text/javascript" src="sha1.js"></script>
		<script type="text/javascript">
			function hashIt() {
				var password = document.getElementById('password').value;
				var ip = document.getElementById('ip').value;
				var randomnumber = <?php echo $_SESSION['RND']; ?>;
				
				document.getElementById('password').value = "";
				document.getElementById('hash').value = hex_sha1((ip + hex_sha1(password) + randomnumber));
			}
		</script>
	</head>
	
	<body>
		<div class="header"></div>
		<div class="container">
			<?php
				if(!empty($msg)) {
					showMsg($msg, $type);
					$msg = null;
					$type = null;
				}
			?>
			<form method="post" action="" onSubmit="hashIt();">
				<table>
					<tr>
						<td>Gebruikersnaam:</td><td><input type="text" name="username"></td>
					</tr>
					<tr>
						<td>Wachtwoord:</td><td><input type="password" id="password"></td>
					</tr>
					<tr>
						<td>&nbsp;</td><td style="text-align: right;"><input type="submit" name="logIn" value="Aanmelden"></td>
					</tr>
				</table>
				<input type="hidden" name="ip" id="ip" value="<?php echo $_SERVER['REMOTE_ADDR']; ?>">
				<input type="hidden" name="hash" id="hash">
			</form>
		</div>
		<div class="footer"><div style="padding: 6px;">&copy; Debaere Brecht</div></div>
	</body>
</html>

Thanks :), please post comments on it

Recommended Answers

All 23 Replies

Thats a really nice script. However what if the user has javascript turned off? Some of your validation is in javascrit therefore a login would be imossible.

Use <noscript> to let the user know their login wont be sucessfull unless they turn it on. Also, are you cleaning your strings from quotes and other characters? I cant really see if you are?

On the other hand, this is a really nice method. It works against CSRF and XXS therefore is really safe. I'm copying the code for personal use so thankyou :D

Does this help ?

Thats a really nice script. However what if the user has javascript turned off? Some of your validation is in javascrit therefore a login would be imossible.

Use <noscript> to let the user know their login wont be sucessfull unless they turn it on. Also, are you cleaning your strings from quotes and other characters? I cant really see if you are?

On the other hand, this is a really nice method. It works against CSRF and XXS therefore is really safe. I'm copying the code for personal use so thankyou :D

Does this help ?

Oh I should indeed implement htmlentities use, but...
What do you mean with CSRF and XXS? What are those things? :) Googling those things gives me other results than I need

CSRF: Cross-site request forgery
XXS: Cross-site Scripting

As mentioned by Designer_101, I would suggest not using POST values directly into SQL queries (Or for anything really) They should be cleaned first. Use a preg_match on any values (such as usernames) where you know they will only contain certain characters, at a minimum you should have addslashes in there.

I am kinda stuck now on how I would make one user logged in over multiple pages, however leaving hackers out. I can't really use sessions because they can be hacked no?
Can I have some assist on this please, then I can complete the script and I'll paste it here :)

Thanks

You will not make it 100% secure, no matter what you do.

But you can take some steps to secure your application further. Sessions are much more secure than cookies and are probably the best way to go for this, you can look at making it harder to 'hack':

Check the User Agent for each visit, while the user agent reported can be masked or changed by the user, checking it for each page load will stop some attempts, so if the user agent suddenly changes mid session, this will stop it:

<?php
if(!isset($_SESSION['user_agent'])) {
    // Set the session value as the hash of the UA
    $_SESSION['user_agent'] = md5($_SERVER['HTTP_USER_AGENT']);
} else {
    // Check that the session value matches the hash of the UA
    if($_SESSION['user_agent'] != md5($_SERVER['HTTP_USER_AGENT'])) {
        // Alert the user they have been logged out due to a UA change
        echo "The user agent data sent by your browser has changed unexpectedly, please login again.";
        session_destroy();
        exit(0);
    }
}
?>

The same (or similar) could be done for other values such as the IP address, but since some users will be behind proxies, and the IP can change mid session, this may not be such a good idea.

Also, to make it harder for a malicious user, you can change the session key every page load:

<?php
session_start();
// We need to copy the old session data
$previousSession = $_SESSION;

// Then re-create a new session
session_destroy();
session_start();

// And finally, reassign the session data
$_SESSION = $previousSession;
?>

Excuse me, I'm not so familiar with sessions. I don't know anything about session keys.
If I put the hash in a session and compare it to the hash in the database. Then it is possible for a hacker to pose himself like the user by somehow stealing the session, yes? How would I go to prevent this? Or isn't this possible?

Seeing as all the session values are stored on the server, unlike cookies which are stored on the clients computer, they are much more secure anyway.

In theory if the malicious user got the session key they may be able to do some things, but this will be prevented to a certain extent by using the script above to change the session key every page load.

Also, make sure your logout button/link is easy to see, as by clicking this the session data should be deleted by the script meaning that it can no longer be accessed.

Seeing as all the session values are stored on the server, unlike cookies which are stored on the clients computer, they are much more secure anyway.

In theory if the malicious user got the session key they may be able to do some things, but this will be prevented to a certain extent by using the script above to change the session key every page load.

Also, make sure your logout button/link is easy to see, as by clicking this the session data should be deleted by the script meaning that it can no longer be accessed.

Isn't it possible to check if the hacker tries to inject a session id in the URI by using GET? Like this:

if($_GET['PHPSESSID'] != null) {
  // A hacker is trying to inject a session ID
}

?

I don't mean to sound rude, but I just want this question answered about blocking session ID stealing.

Well, to be honest when your asking if somethings secure it either is or it isnt. Theres no in the middle.

As another user said, there are however ways in which you can make your website 'safer'. Depending on the trafic of your website you should increase the amount of security you add to your code.
For example, I run a low traffic website for a sports team, and the only security is that of protecting against SQL injections (by clearing all inputed data).

To continue, it is therefore your choice wether or not you need this much security but daniweb provides answer, and the answers above are perfectly in context and should be appreciated. Sorry if it sounds blunt but people spend time writing posts to help others, not for the fun of it.

In an earlier comment you said you didnt understant CSRF.
In this context it would be validating a selfmade html form on a victims website.
In others words, creating a form with the same names as those of the website your hacking and then sending it to the website to be validated. Its a very sneakly thing and I suggest you look into it, google will help you there.

Hope all this helps

I don't mean to sound rude, but I just want this question answered about blocking session ID stealing.

The code I posted earlier would help prevent this. It will changed the session ID every single time the user clicks on a new link, so even if the malicious user somehow gets the session ID, it will likely have changed by the time they try to do something.

Here is the code again:

<?php
session_start();
// We need to copy the old session data
$previousSession = $_SESSION;

// Then re-create a new session
session_destroy();
session_start();

// And finally, reassign the session data
$_SESSION = $previousSession;
?>

The code I posted earlier would help prevent this. It will changed the session ID every single time the user clicks on a new link, so even if the malicious user somehow gets the session ID, it will likely have changed by the time they try to do something.

Here is the code again:

<?php
session_start();
// We need to copy the old session data
$previousSession = $_SESSION;

// Then re-create a new session
session_destroy();
session_start();

// And finally, reassign the session data
$_SESSION = $previousSession;
?>

instead of all that, why not use session_regenerate_id()?

thats what i use.

Ok, I will rewrite the whole code and upload it here once ready.

session_regenerate_id() would be better in this case.

i agree with designer_101

Ok this is the code I have now, I haven't fully checked it yet so there could be some errors in it, but I doubt this. What I'm more interested in is are the holes in the code for a user to hack it. Well... if it's quite secure, or if there are improvements to be made.
It works the same as before, with some changes. The function renew() should be called on every page load as suggested here.

login_BackEnd.php

<?php
	/*
	 * Error Codes
	 * 0: Success
	 * 1: User does not exist in DB
	 * 2: User is already logged in
	 * 3: Hash in form is not equal to server side created hash
	 * 4: An error occured while creating the session in the DB
	*/
	session_start();
	srand(time());
	
	if(!isset($_SESSION['RND'])) {
		$_SESSION['RND'] = sha1(rand()%1000001);
	}
	
	if(!isset($_SESSION['authINF1'], $_SESSION['authINF2'], $_SESSION['authINF3'])) {
		$_SESSION['authINF1'] = sha1($_SERVER['HTTP_USER_AGENT']);
		$_SESSION['authINF2'] = sha1($_SERVER['HTTP_ACCEPT_LANGUAGE']);
		$_SESSION['authINF3'] = sha1($_SERVER['REMOTE_ADDR']);
	}
	
	function authenticate() {
		if(!isset($_SESSION['conSession'], $_SESSION['chSession'])) {
			// Get information from form
			$username = htmlentities($_POST['username'], ENT_QUOTES);
			$hash = htmlentities($_POST['hash'], ENT_QUOTES);
			
			// Create salt for hash
			$salt = htmlentities($_SESSION['RND'], ENT_QUOTES);
			$_SESSION['RND'] = sha1(rand()%1000001);
			$salt .= htmlentities($_SERVER['REMOTE_ADDR'], ENT_QUOTES);
			$salt .= htmlentities($_SERVER['HTTP_USER_AGENT'], ENT_QUOTES);
			
			$qGetUser = @mysql_query("SELECT * FROM users WHERE gebruikersnaam='".$username."'");
			if(@mysql_num_rows($qGetUser) == 1) {
				
				// The user exists in the DB
				$aGetUser = @mysql_fetch_assoc($qGetUser);
				$qGetSession = @mysql_query("SELECT * FROM sessions WHERE gebruikersnaam='".$username."'");
				if(@mysql_num_rows($qGetSession) == 0) {
					
					// The user is not logged in yet
					$serverSideHash = sha1($aGetUser['wachtwoord'].$salt);
					if($serverSideHash == $hash) {
						
						// The submitted hash and the server side created one are equal
						$chSession = sha1(rand()%1000001);
						if(@mysql_query("INSERT INTO sessions(gebruikersnaam, conSessie, chSessie, sessieTijd) VALUES('".$username."', '".$serverSideHash."', '".$chSession."', ".time().")")) {
							
							// The session has been created
							$_SESSION['conSession'] = $serverSideHash;
							$_SESSION['chSession'] = $chSession;
							$err = 0;
						}
						else {
							$err = 4;
						}
					}
					else {
						$err = 3;
					}
				}
				else {
					$err = 2;
				}
			}
			else {
				$err = 1;
			}
		}
		return $err;
	}
	
	function renew() {
		deleteOldSessions();
		session_regenerate_id(TRUE);
		$conSession = htmlentities($_SESSION['conSession'], ENT_QUOTES);
		$chSession = htmlentities($_SESSION['chSession'], ENT_QUOTES);
		$qGetSession = @mysql_query("SELECT * FROM sessions WHERE conSessie='".$conSession."' AND chSessie='".$chSession."'");
		
		if(@mysql_num_rows($qGetSession) == 1) {
			$aGetSession = @mysql_fetch_assoc($qGetSession);
			if($chSession == $aGetSession['chSessie']) {
				$chSession = sha1((rand()%1000001).$chSession);
				$_SESSION['chSession'] = htmlentities($chSession, ENT_QUOTES);
				@mysql_query("UPDATE sessions SET chSessie='".$chSession."', sessieTijd=".time()."");
			}
		}
	}
	
	function destroy() {
		$conSession = htmlentities($_SESSION['conSession'], ENT_QUOTES);
		$chSession = htmlentities($_SESSION['chSession'], ENT_QUOTES);
		$qGetSession = @mysql_query("DELETE FROM sessions WHERE conSessie='".$conSession."' AND chSessie='".$chSession."'");
		session_unset();
		session_destroy();
	}
	
	function deleteOldSessions() {
		$inactivityTime = 60*5;
		$expirationTime = time() - $inactivityTime;
		
		if(isset($_SESSION['conSession']) && isset($_SESSION['chSession'])) {
			$conSession = htmlentities($_SESSION['conSession'], ENT_QUOTES);
			$chSession = htmlentities($_SESSION['chSession'], ENT_QUOTES);
			$qGetSession = @mysql_query("SELECT * FROM sessions WHERE conSessie='".$conSession."' AND chSessie='".$chSession."' AND sessieTijd<".$expirationTime."");
			$aGetSession = @mysql_fetch_assoc($qGetSession);
			if(@mysql_num_rows($qGetSession) == 1) {
				destroy();
			}
		}
		@mysql_query("DELETE FROM sessions WHERE sessieTijd<".$expirationTime."");
	}
?>

login.php

<?php
	include_once("login_BackEnd.php");
	include_once("connect.php");
	deleteOldSessions();
	
	if(isset($_POST['logIn'])) {
		$response = authenticate();
		switch($response) {
			case 0:
				$msg = "Succes";
				$type = "notification";
				break;
			default:
				$msg = $response;
				$type = "information";
				break;
		}
	}
	
	if(isset($_SESSION['conSession'], $_SESSION['chSession'])) {
		if((sha1($_SERVER['HTTP_USER_AGENT']) == $_SESSION['authINF1']) && (sha1($_SERVER['HTTP_ACCEPT_LANGUAGE']) == $_SESSION['authINF2']) && (sha1($_SERVER['REMOTE_ADDR']) == $_SESSION['authINF3'])) {
			$conSession = htmlentities($_SESSION['conSession'], ENT_QUOTES);
			$chSession = htmlentities($_SESSION['chSession'], ENT_QUOTES);
			$qGetSession = @mysql_query("SELECT * FROM sessions WHERE conSessie='".$conSession."' AND chSessie='".$chSession."'");
			$aGetSession = @mysql_fetch_assoc($qGetSession);
			if(@mysql_num_rows($qGetSession) == 1) {
				$msg = "U bent ingelogd als ".$aGetSession['gebruikersnaam'];
				$type = "notification";
				renew();
			}
		}
	}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
		<title>Vermeersch Constructie</title>
		<script type="text/javascript" src="MooTools_Functions.js"></script>
		<script type="text/javascript" src="MooTools_BackEnd.js"></script>
		<!--[if lt IE 7.]>
			<script defer type="text/javascript" src="pngfix.js"></script>
		<![endif]-->
		<link rel="stylesheet" href="style.css" type="text/css">
		<script type="text/javascript" src="sha1.js"></script>
		<script type="text/javascript">
			function hashIt() {
				var salt = "<?php echo htmlentities($_SESSION['RND'], ENT_QUOTES); ?>";
				salt += "<?php echo htmlentities($_SERVER['REMOTE_ADDR'], ENT_QUOTES); ?>";
				salt += "<?php echo htmlentities($_SERVER['HTTP_USER_AGENT'], ENT_QUOTES); ?>";
				pass = document.getElementById('password').value;
				
				document.getElementById('password').value = "";
				document.getElementById('hash').value = hex_sha1(hex_sha1(pass)+salt);
			}
		</script>
	</head>
	
	<body>
		<div class="header"></div>
		<div class="container">
			<?php
				if(!empty($msg)) {
					showMsg($msg, $type);
					$msg = null;
					$type = null;
				}
			?>
			<form method="post" action="" onSubmit="hashIt();">
				<table>
					<tr>
						<td>Gebruikersnaam:</td><td><input type="text" name="username"></td>
					</tr>
					<tr>
						<td>Wachtwoord:</td><td><input type="password" id="password"></td>
					</tr>
					<tr>
						<td>&nbsp;</td><td style="text-align: right;"><input type="submit" name="logIn" value="Aanmelden"></td>
					</tr>
				</table>
				<input type="hidden" name="hash" id="hash">
			</form>
		</div>
		<div class="footer"><div style="padding: 6px;">&copy; Debaere Brecht</div></div>
	</body>
</html>

why are you using javascript at all in the login process? I mean, only use it for basic validation not hashing. That in itself is a security flaw because attackers can see how you are encrypting a password, which helps them to crack it.

What if a user has javascript turned off? Then what. You should have php handle everything.

why are you using javascript at all in the login process? I mean, only use it for basic validation not hashing. That in itself is a security flaw because attackers can see how you are encrypting a password, which helps them to crack it.

What if a user has javascript turned off? Then what. You should have php handle everything.

I agree JavaScript is more efficient (for the users) but PHP should always be your focus as it is loaded before the hacker gets the page.

Always make sure with important scripts that you make sure that the refering URL is the the page you wish it to come from.

why are you using javascript at all in the login process? I mean, only use it for basic validation not hashing. That in itself is a security flaw because attackers can see how you are encrypting a password, which helps them to crack it.

What if a user has javascript turned off? Then what. You should have php handle everything.

I use javascript for client side hashing before the data is posted because I read somewhere that the postback or something could be hacked. I don't have the link on me but I might find it

I use javascript for client side hashing before the data is posted because I read somewhere that the postback or something could be hacked. I don't have the link on me but I might find it

Things may be hacked, but you are basically giving the user the key to a door.. Rather than having to work out what you are doing to a string, they know exactly what is happening.

Also, some people have javascript off, meaning that the string will not be hashed.

You can do the same thing in PHP, much more secure to do it that way as the end user should then never find out what you are doing.

http://www.google.nl/search?hl=nl&c2coff=1&q=intercepting+HTTP+requests&btnG=Zoeken&meta=
This is the link that is mentioned in a thread about postback hacking. It's a dutch thread so I'll just say what's mentioned there:
"Hackers are able to intercept data you send to the server. If they get to intercept the postback (see notes) then the password is openly in front of them. How do we solve this? By hashing the password on the client."
I guess that it's a downside that the method of encryption is visible, however, all they have is the encryption method and the encrypted password. And to decrypt it, that's another something.

My intentions are not to make you feel bad about your post, I'm just explaining why I used the javascript method. According to the thread I used about login systems; which method would you use? I don't know anything about this stuff that's why I looked it up. Maybe you can elaborate on how I'm giving them a key.
And the login will just require javascript to be turned on, if not they will get a message saying it's not.

Anyway, thanks for all the replies, hope to get one soon again.

If you have information before hand, it makes tasks easier, right? If a hacker knows how you hashed a password, then it makes it easier for them to get around it. The less they know, the harder it is to hack something.

I have actually never seen anything where a hacker has intercepted a http request. Its never been a major issue and my Internet Security Officer at work would make us prevent against it, if it was a viable threat.

If you are so worried, look into secure socket layer protection (ssl). It encrypts all data between the server and the client. Never use js to hash anything. I understand why you are doing it, but if a user disables javascript, the hacker can see the plain text password anyway. Its not going to help out much.

I really don't recommend making a user have javascript enabled to login. There are actually a lot of people that disable js when they login to a website. It helps prevent hackers to run malicious scripts to steal and change your data in an account. This is known as CSRF or XSRF. It is good practice to do this.

You should turn your focus to hashing the password on the php side of things. No one can change that.

As for other security factors you should look into, you might want to use a token system and timed forms. These help with CSRF.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.