Tag Archives: php

Persistent sessions with PHP

Persistent sessions is a set of mechanisms created in php that allow authentication to persist across multiple browser sessions (ie closing down the browser). Any session variables you set in php are destroyed and cant be used in further sessions. This means annoyingly that your users must then sign into you site again. Their are some security issues you must be aware of when implementing a system like this.

Persistent sessions weakens the security of your web site, issues like it being accessed on a public computer (you could add a tick box to be remembered) or if you hold security sensitive information. Its a trade off between usability and security. You could even implement a two step security system where by for example you may trust the user to carry out certain procedures from a persistent session such as adding a item to a wish list but require full password validation for procedures such as purchasing a item from a stored credit card number or changing passwords.

When thinking about how to create a system is seems tempting enough just to store the username and password in the cookie and read them off when the user comes back to the web site and automatically log them in. This is bad very idea. A potential hacker could easily gain access to this information and replay it back to your server and gain unauthorised access.

The cookie is the only way standard way to persist data across multiple sessions. So we must store information in this cookie that will provide us with the information in the future to re authenticate the user. Clearly is a security risk so the best we can do is store information that will only be useful for a certain period of time. It would be inadvisable to provide any with permanent access this way.

In the cookie we are going to store 2 pieces of information, a hash code representing the username, we’ll call this a identifier and a key (or token) that is valid only for certain period of time and is regenerated after one use. To create a hash of the username we could do something like this:

$salt = "pAulR2";
$identifier = md5( $salt . md5($username . $salt ) );

This should be stored in your database alongside your other user details. Remember to always to use some form of ‘salt‘ when creating hash’s. This is just a string that is known only to your application and kept secret. This insures that people cant use rainbow tables or take a few educated guesses to reverse the hash.

Next we must create a key (or token), this will be like our temporary password, valid for a certain period of time. We just need a long a string that isn’t predictable. One could be generated like this:

$key = md5(uniqid(rand(), true);

Again we would need to store this in the database and associate it to the correct user along with another field recording the time span for which it is valid.

Outline Example

Ok, you’ve authenticated your users username and password and they’ve indicated that they wish to remain logged in for one week.

// This examples assumes you have already connected to a MySQL database
$salt = "pAulR2";
/*
  You must create a identifier for every user and store it in the database prior implementing this.
  $identifier = md5( $salt . md5($username . $salt ) );
*/
// retrieve the $identifier from the database, I'm going to assume its in a variable called
// $user['identifier']
$identifier = $user['identifier'];

// create a random key
$key = md5(uniqid(rand(), true);
// calculate the time in 7 days ahead for expiry date
$timeout = time() + 60 * 60 * 24 * 7;

// Set the cookie with information
setcookie('authentication', "$identifier:$key", $timeout);
// now update the database with the new information
$result = mysql_query("UPDATE `user` SET key = '$key', timeout = '$timeout'
WHERE username = '$username'");
if (!$result) {
    die('Invalid query: ' . mysql_error());
}

Right that takes care of setting the cookie but know we must authorise it when its been detected when the user comes back to the site. On the users first visit you need to implement code like the following:

$salt = "pAulR2";

if (isset($_COOKIE['authentication'])) {
    // cookie is set, lets see if its vailed and log someone in
    $clean = array();
    $mysql = array();

    $now = time();

    list($identifier, $token) = explode(':', $_COOKIE['authentication']);
    if (ctype_alnum($identifier) && ctype_alnum($token)) {
	$clean['identifier'] = $identifier;
	$clean['key'] = $key;

	$mysql['identifier'] = mysql_real_escape_string($clean['identifier']);

	$result = mysql_query("SELECT * FROM user
                                       WHERE identifier = '{$mysql['identifier']}'");
	if (mysql_num_rows($result)) {
		$record = mysql_fetch_assoc($result);
		if ($clean['key'] != $record['key']) {
			// fail because the key doesn't match
		}elseif ($now > $record['timeout']){
			// fail because the cookie has expired
		}elseif ($clean['identifier'] != md5($salt.md5($record['userID'].$salt))){
			// fail because the identifiers does not match
		}else{
			/*
                          Success everything matches, now you can process
                          your login functions. The key must be re generated
                          for the next login. But don't increase the timeout to
                          ensure that the user must login in once the time
                          period has passed.
                       */
	        }
	}
	}else {
			/* failed because the information is not in the
                            correct format in the cookie */
        }
}

And remember once you have finished with the cookie delete it!

Sources: Essential PHP Security

Essential Free PHP Web Development Tools

As a PHP web developer I’ve tried many different tools to aid my work. There are many tools out there to try and I encourage you to do so, but I have complied a list of what consider essential to make your job as easy as possible. If anyone has any useful tools/sites I haven’t listed let me know.

PHP

xampp - A cross platform web server including PHP and MySQL along with other tools such as Perl. Easily installs on a Windows machine (though it is cross platform), I recommend the lite version and install it on USB pen for portability.

phpMyAdmin - A web based tool for administrating MySQL databases, makes setting them up easy. The new version even includes a query builder. This is included with xampp just go to  http://localhost/phpmyadmin.

Zend Framework – A open source object oriented PHP5 framework. Contains easy to use classes to create a modern web app. Has support for many web services such as flickr and Google.

phpclasses – This a web site with a massive database of PHP classes. Always look her first as its probably already been done to a high standard.

Web browsers

FireFox 3.0 - An open source web broswer that supports loads of useful plugins for web developers. Not to mention generally how much better it is then any other.

Web Developer – One of the best FireFox plugins ever made. Contains a massive array of tools to help you develop your web site. From window resizing to editing CSS on the fly. Just try it!

IE Tab (Windows only) - A plugin for FireFox that allows you easily to switch your browser window to the Explorer rendering engine. As this is the most widely used browser you have to check your app works in it.

Multiple IE (Windows XP only) – A great little download that gives you old Internet Explorers (6.0, 5.5, 5, 4, 3) to try your site in. If you like me and upgraded to IE7, how do you check your site in IE6, many people still use it. Pity it doesn’t work on Vista.

Safari - Some people use this as a web browser, I guess now its available on windows you could check your site works in it

Text Editors

notepad++ – A free cross platform text editor with useful add-ons such as code highlighting for every language imaginable.

Other web tools

X Ray - Just add it to your bookmarks and you ready to go, click it, it loads then you can click any element on a page and it will show you the ‘CSS box model’. Very useful when your having pixel problems.

Browser Shots - Check you new web site in every browser known to man on any platform in one go. Warning: it doesn’t always return everything you ask for and it can take a while.

FireFtp – A FireFox plugin, a simple and realiable FTP program, can manage multiple accounts.

JavaScript & Ajax

Firebug – A plugin for FireFox, before this you would go mad debugging your JavaScript with nothing to go on. This provides full details on all errors and detailed information on Ajax requests. I also use it to be nosey on other web sites to see how they work.

JQuery - A fast lightweight JavaScript framework. Makes Ajax and HTML manlipation very easy for a modern 2.0 app.

Interface for jQuery – An additional set of jQuery plugins that provide a mass of additioal functionality for jQuery.

UI for jQuery – More plugins for jQuery that provide more for jQuery. I believe that interface and UI are being merged.

Visual jQuery – The doucmentation for jQuery can be a little rubbish, this web site lays it out in a simple format to effectivly use it.

Dates functions with MySQL and unix timestamp

MySQL has powerful functions built in to manipulate data with dates. These functions only work within MySQL providing your using their own date/time stamp. If your like me, your much rather use the PHP function time() to get the Unix timestamp and store that in the database. It seems a shame to miss out on these features because we use Unix time. Never fear, the following examples shows how to get round this using FROM_UNIXTIME(). So what happens now if we want to SELECT or GROUP data by periods, such as by week, month or year. Take one look in the MySQL manual and there are few practical examples on how this can be done using only the Unix timestamp. Below I run through a practical example and how this can easily be achieved. If you want a mock up table to try it out, run the following SQL. This table is a vastly simplified version of a e-commerce system order table, but the logic here could be applied to any type of database driven web site.

CREATE TABLE `orders` (
`order_ID` int(11) NOT NULL auto_increment,
`timeordered` int(11) NOT NULL default '0',
`grandtotal` double(10,2) NOT NULL default '0.00'
PRIMARY KEY  (`order_ID`)) ;

This table contains the Unix timestamp in `timeordered` and `grandtotal` contains a decimal number. We are going to construct a SQL query that will list the number of orders in a month, the total amount of all those orders and the average value of those orders. Lines 1, 2, 4 are standard functions used to calculate our values. The interesting lines are 5 and 6. If I was using the normal MySQL timstamp and could simply write GROUP BY MONTH(), YEAR() to achieve the results we want. You may ask why are we using both MONTH() and YEAR() when we just need it to group by MONTH(). Well the answer is you could just use MONTH(), but that would only ever return 12 rows (1 for each month) no matter over how many years. So for example it would group January 2004, January 2005 or any other January. For some systems this would be undesirable. Using YEAR() ensure only one month and one year is in one return row.

SELECT COUNT(order_ID) as `number`,
SUM(grandtotal) as `cgrandtotal`,
FROM_UNIXTIME(timeordered, '%M %Y') as `fdate`,
AVG(grandtotal) AS `averagegrand`
FROM `orders`
GROUP BY MONTH(FROM_UNIXTIME(timeordered,'%Y-%m-%d %H.%i.%s')),
YEAR(FROM_UNIXTIME(timeordered,'%Y-%m-%d %H.%i.%s'))
ORDER BY timeordered DESC

Normally we put the column name in the brackets of the function of the MySQL date (ie YEAR(column-name)). But because we are using Unix time we must use the function FROM_UNIXTIME(column-name-of-unixtime, format). The format parameter simply specifies how to turn the Unix time to a understandable timestamp for MySQL, the ones shown on lines 7 and 8 seem to work fine, I believe there may be shorter ones that can be used.

So the query above will return something like this (after adding your own dummy data!):

number cgrandtotal fdate averagegrand
8 532.00 January 2008 66.50
21 1201.20 December 2007 57.20
17 1041.25 November 2007 61.25

The example below assumes you have some basic knowledge of SQL and has been tested on MySQL 4.1.