User Login Script

#1

As requested, a user-login tutorial. This tutorial will show you a basic user login script, how it is set up and how it works. It’s going to run on cookies (simply because I haven’t mastered sessions yet :stuck_out_tongue:

One little quirk of this script is that it doesn’t check whether or not a user visiting the signup page is already logged in. It’s a good exercise if you’re not too fluent in PHP to alter this script to do so ;)

The Login Page
Naturally it’s not mandatory to have a login page, but we’ll need a snippet of code that deals with logging users into your website. This may very well be embedded in an already existing (PHP) page on your website. What needs to be done is checking against the login credentials, and handle the course of action from there: either display a message or set a cookie. I will not discuss the session mechanism here, but you’re free to implement that instead.

The fields we will have coming in are:

- Loginname (textbox)
- Loginpass (password textbox)

These can be located on any page on your website, but what’s important is that the form they’re in, is pointing to the webpage that handles logging in. Still with me? Below is the snippet you can use to log users in.

<?php

// Retrieve submitted data from the form, if applicable
$loginname = "";
if (isset($_POST['loginname'])) {
	$loginname = trim($_POST['loginname']);
}

$loginpass = "";
if (isset($_POST['loginpass'])) {
	$loginpass = trim($_POST['loginpass']);
}

// Check for any inconsistencies
if ($loginname == "" OR $loginpass == "") {
	// One or both fields have not been filled in
	// Error message here

} else {
	// Hash the password
	$password = md5($loginpass);

	// Open a database connection
	$db = mysql_connect("yourmysqlhost", "yourmysqluser", "yourmysqlpass") or die("No connection to MySQL Host made");
	mysql_select_db("yourmysqldatabase");

	// Check if the username exists
	$sql = "SELECT userid FROM users WHERE username = '".$username."'";
	$resultset = mysql_query($sql) or die("SQL Error: ".mysql_error());
	if (mysql_num_rows($resultset) == 0) {
		// Username does not exist
		// Error message here
	}

	// We're done in the database, so connection can be closed
	mysql_close($db);

	// Set a cookie for the user
	$userid = mysql_fetch_assoc($resultset);
	$cookievalue = $userid."_".md5($loginname.$password);
	setcookie("userhash", $cookievalue), time() + 3600);

	// The user has succesfully been logged in (if no SQL errors pop up)
	// Display notification here
	// Be sure to redirect the user, or provide a link to
	// the login/home page of your website
}

?>

As with the signup page, I have compiled this code on-the-fly, with every security hole and exploit intact. Please walk through it with a critical eye before implementing it (especially in production environments/live sites). One thing to keep in mind is that setcookie() will set a cookie, but this cookie won’t be picked up on by the script until the next page refresh. This is something I’d like to work around by giving a ‘You are now logged in’ message in a separate page, which automatically redirects after several seconds. It’s also possible of course to use header("Location: "), but that’s really a matter of preference. Just keep this in mind when your script ‘doesn’t log you in’.

Checking if the user is logged in
Last but not least, we’ll have to check whether or not the user is actually logged in. PHP provides the $_COOKIE superglobal for fetching cookie values. One sidenote to make is that $_COOKIE is -also- user input, and therefore that its value is by default unreliable. Look up ‘session stealing’ on your favorite search engine to learn about the dangers of cookies and sessions in user input.

The below code is also a snippet, just like the login page snippet. It should be included in any and all pages on which you wish to check the login status of the visitor.

<?php

// Fetch the cookie value, if available
$userid = 0;
$cookiehash = "";
if (isset($_COOKIE['userhash'])) {
	$cookievalue = trim($_COOKIE['userhash']);
	$myvalues = explode("_", $cookievalue);

	if (count($myvalues) == 2) {
		$userid = intval($myvalues[0]);
		$cookiehash = $myvalues[1];
	}
}

// Variable keeping the logged in status
$isLoggedIn = false;

if ($userid > 0 && cookiehash != "") {
	// Open a database connection
	$db = mysql_connect("yourmysqlhost", "yourmysqluser", "yourmysqlpass") or die("No connection to MySQL Host made");
	mysql_select_db("yourmysqldatabase");

	// Check if the username exists
	$sql = "SELECT username, password FROM users WHERE userid = '".$userid."'";
	$resultset = mysql_query($sql) or die("SQL Error: ".mysql_error());
	if (mysql_num_rows($resultset) == 0) {
		// Username does not exist
		// Error message here

	} else {
		$dbinfo = mysql_fetch_assoc($resultset);
		$dbhash = md5($dbinfo['username'], $dbinfo['password']);

		if ($dbhash == $cookiehash) {
			$isLoggedIn = true;
		}
	}

	// We're done in the database, so connection can be closed
	mysql_close($db);
}

?>

The variable $isLoggedIn can be used to enable logged-in-only content on your website (such as a welcome message, or a logout button). Take a long and hard look at what I’m doing with the cookie value (both in setting it in the login script, as in this checking script). I’m applying -some- security measures to make sure it’s not blatantly obvious how to change the value so I become someone else: simply setting the userid for example wouldn’t be sufficient for obvious reasons. Best would be to add a socalled ‘salt’: a few random characters added to the username/password before hashing. An alternative is to add a timestamp before hashing. This will make the hash even more random and harder to crack by malicious users. How safe and complex you want the cookie value to be, is entirely up to you though, and whether the complexity lives up to its necessity is subject to the ultimate goal of your application.

#2

So, under the assumption this is implemented as it is here, how secure is this? And what other necessary security measures do I need to make? Thanks soooo much!

#3

The above implementation is susceptible for SQL injection and password sniffing. It could be open for more exploits, but right now, those are the two main concerns that I can see. SQL injection can be mitigated by using mysql_real_escape_string() on where you use user input ($username for example) in SQL queries. Password sniffing is a bit tougher to mitigate though. It has to do with sending the password plaintext over the network. It’s possible to implement your site using SSL security (port 443 / HTTPS) or to encrypt/hash the password before having it sent to the server (MD5 in JavaScript for example).