Cookies, Sessions, and Authentication


As your web projects grow larger and more complicated, you will find an increasing need to keep track of your users. Even if you aren’t offering logins and passwords, you will still often need to store details about a user’s current session and possibly also recognize him/her when he/she returns to your site.

Several technologies support this kind of interaction, ranging from simple browser cookies to session handling and HTTP authentication. 

Using Cookies in PHP

A cookie is an item of data that a web server saves to your computer’s hard disk via a web browser.

Importance of using cookies:

1.It helps in session tracking
2.Maintaining data across multiple visits
3.Holding shopping cart contents
4.Storing login details

Setting a Cookie

Setting a cookie in PHP is a simple matter. As long as no HTML has yet been transferred, you can call the setcookie function, which has the following syntax:

setcookie(name, value, expire, path, domain, secure, httponly);

name: The name of the cookie. This is the name that your server will use to access the
cookie on subsequent browser requests. e.g:Username

value :The value of the cookie, or the cookie’s contents. This can contain up to 4 KB of
alphanumeric text. eg:Hannah

expire (Optional) :Unix timestamp of the expiration date. Generally, you will probably use
time() plus a number of seconds. If not set, the cookie expires when the browser
closes.eg:time() + 2592000

path (Optional): The path of the cookie on the server. If this is a / (forward slash), the
cookie is available over the entire domain, such as www.rbgtech.blogspot.com If it is a
subdirectory, the cookie is available only within that subdirectory. The default is
the current directory that the cookie is being set in, and this is the setting you will
normally use. eg: /

domain (Optional):The Internet domain of the cookie.

So, to create a cookie with the name username and the value Hannah that is accessible across the entire web server on the current domain, and will be removed from the browser’s cache in seven days, use the following:
setcookie('username', 'Hannah', time() + 60 * 60 * 24 * 7, '/');

Accessing a Cookie

Reading the value of a cookie is as simple as accessing the $_COOKIE system array. For
example, if you wish to see whether the current browser has the cookie called user name already stored and, if so, to read its value, use the following:
if (isset($_COOKIE['username'])) $username = $_COOKIE['username'];


Note that you can read a cookie back only after it has been sent to a web browser.
This means that when you issue a cookie, you cannot read it in again until the browser reloads the page (or another with access to the cookie) from your website
and passes the cookie back  to the server in the process.

Destroying a Cookie


To delete a cookie, you must issue it again and set a date in the past. It is important for all parameters in your new setcookie call except the timestamp to be identical to the parameters when the cookie was first issued; otherwise, the deletion will fail.
Therefore, to delete the cookie created earlier, you would use the following:
setcookie('username', 'Hannah', time() - 2592000, '/');

As long as the time given is in the past, the cookie should be deleted. However, I have used a time of 2,592,000 seconds (one month) in the past in case the client computer’s date and time are not correctly set.


HTTP Authentication

HTTP authentication uses the web server to manage users and passwords for the application. It’s adequate for most applications that ask users to log in.

PHP authentication example

<?php
 if (isset($_SERVER['PHP_AUTH_USER']) &&
 isset($_SERVER['PHP_AUTH_PW']))
 {
 echo "Welcome User: " . $_SERVER['PHP_AUTH_USER'] .
 " Password: " . $_SERVER['PHP_AUTH_PW'];
 }
 else
 {
 header('WWW-Authenticate: Basic realm="Restricted Section"');
 header('HTTP/1.0 401 Unauthorized');
 die("Please enter your username and password");
 }
?>



How the code works:

The first thing the program does is look for two particular array values:
$_SERVER['PHP_AUTH_USER'] and $_SERVER['PHP_AUTH_PW']
If they both exist, they represent the username and password entered by a user into an authentication
prompt
Basic realm is the name of the section that is protected and appears as part of the pop-up prompt:
WWW-Authenticate: Basic realm="Restricted Area"
If the user fills out the fields, the PHP program runs again from the top. But if the user clicks the Cancel button, the program proceeds to the following two lines, which send the following header and an error message:
HTTP/1.0 401 Unauthorized
The die statement causes the text “Please enter your username and password” to be displayed.



PHP authentication with input checking

<?php
 $username = 'admin';
 $password = 'letmein';
 if (isset($_SERVER['PHP_AUTH_USER']) &&
 isset($_SERVER['PHP_AUTH_PW']))
 {
 if ($_SERVER['PHP_AUTH_USER'] == $username &&
 $_SERVER['PHP_AUTH_PW'] == $password)
 echo "You are now logged in";
 else die("Invalid username / password combination");
 }
 else
 {
 header('WWW-Authenticate: Basic realm="Restricted Section"');
 header('HTTP/1.0 401 Unauthorized');
 die ("Please enter your username and password");
 }
?>

Storing Usernames and Passwords


Obviously, MySQL is the natural way to store usernames and passwords. But again,we don’t want to store the passwords as clear text, because our website could be compromised if the database were accessed by a hacker. Instead, we’ll use a neat trick
called a one-way function.

This type of function is easy to use and converts a  string of text into a seemingly random string. 
So now I have moved on to using the PHP hash function, passing it a version of the ripemd algorithm, which was designed by the open academic community and which (like md5) returns a 32-character hexadecimal number—so it can easily replace md5
in most databases. Use it like this:

$token = hash('ripemd128', 'mypassword');
That example happens to give $token this value:
7b694600c8a2a2b0897c719958713619

Salting


Unfortunately, hash on its own is not enough to protect a database of passwords,because it could still be susceptible to a brute force attack that uses another database of known 32-character hexadecimal tokens. Such databases do exist, as a quick Google search will verify, although probably only for md5 and sha1 or sha2 at the moment.

Salting is simply a matter of adding some text that only we know about to each parameter to be encrypted, like this (with the salt highlighted in bold):
$token = hash('ripemd128', 'saltstringmypassword');

In this example, the text saltstring has been prepended to the password. Of course, the more obscure you can make the salt, the better. I like to use salts such as this:
$token = hash('ripemd128', 'hqb$tmypasswordcg*l');

Here some random characters have been placed both before and after the password.Given just the database, and without access to your PHP code, it should now be next to impossible to work out the stored passwords.




Let’s create a MySQL table to hold some user details and add a couple of accounts. So type and save the program in example below as setupusers.php, and then open it in your browser.

Creating a users table and adding two accounts:

 <?php // setupusers.php
 require_once 'login.php';
 $connection =
 new mysqli($db_hostname, $db_username, $db_password, $db_database);
 if ($connection->connect_error) die($connection->connect_error);
 $query = "CREATE TABLE users (
 forename VARCHAR(32) NOT NULL,
 surname VARCHAR(32) NOT NULL,
 username VARCHAR(32) NOT NULL UNIQUE,
 password VARCHAR(32) NOT NULL
 )";
 $result = $connection->query($query);
 if (!$result) die($connection->error);
 $salt1 = "qm&h*";
 $salt2 = "pg!@";
 $forename = 'Bill';
 $surname = 'Smith';
 $username = 'bsmith';
 $password = 'mysecret';
 $token = hash('ripemd128', "$salt1$password$salt2");

 add_user($connection, $forename, $surname, $username, $token);
 $forename = 'Pauline';
 $surname = 'Jones';
 $username = 'pjones';
$password = 'acrobat';
 $token = hash('ripemd128', "$salt1$password$salt2");

 add_user($connection, $forename, $surname, $username, $token);
 function add_user($connection, $fn, $sn, $un, $pw)
 {
 $query = "INSERT INTO users VALUES('$fn', '$sn', '$un', '$pw')";
 $result = $connection->query($query);
 if (!$result) die($connection->error);
 }
?>

PHP authentication using MySQL example

 <?php // authenticate.php
 require_once 'login.php';
 $connection =
 new mysqli($db_hostname, $db_username, $db_password, $db_database);
 if ($connection->connect_error) die($connection->connect_error);
 if (isset($_SERVER['PHP_AUTH_USER']) &&
 isset($_SERVER['PHP_AUTH_PW']))
 {
 $un_temp = mysql_entities_fix_string($connection, $_SERVER['PHP_AUTH_USER']);
 $pw_temp = mysql_entities_fix_string($connection, $_SERVER['PHP_AUTH_PW']);
 $query = "SELECT * FROM users WHERE username='$un_temp'";
 $result = $connection->query($query);
 if (!$result) die($connection->error);
 elseif ($result->num_rows)
 {
 $row = $result->fetch_array(MYSQLI_NUM);
$result->close();
 $salt1 = "qm&h*";
 $salt2 = "pg!@";
 $token = hash('ripemd128', "$salt1$pw_temp$salt2");

 if ($token == $row[3]) echo "$row[0] $row[1] :
Hi $row[0], you are now logged in as '$row[2]'";

 else die("Invalid username/password combination");
 }
 else die("Invalid username/password combination");
 }
 else
 {
 header('WWW-Authenticate: Basic realm="Restricted Section"');
 header('HTTP/1.0 401 Unauthorized');
 die ("Please enter your username and password");
 }
 $connection->close();

function mysql_entities_fix_string($connection, $string)
 {
 return htmlentities(mysql_fix_string($connection, $string));
 }
 function mysql_fix_string($connection, $string)
 {
 if (get_magic_quotes_gpc()) $string = stripslashes($string);
 return $connection->real_escape_string($string);
 }
?>

Explanation of the code

Now all that’s necessary is to check $token against the value stored in the database,which happens to be in the fourth column—which is column 3 when starting from 0.So $row[3] contains the previous token calculated for the salted password. If the two match, a friendly welcome string is output, calling the user by his or her first name.Otherwise, an error message is displayed.