Introduction
HTTP
is a stateless protocol. This means that each request is handled
independently of all the other requests and it means that a server or a
script cannot remember if a user has been there before. However,
knowing if a user has been there before is often required and therefore
something known as cookies and sessions have been implemented in order
to cope with that problem.
On page two we will look at cookies and sessions will be covered on page three. On page four we will talk a bit about security.
Cookies
Cookies are small text files placed on the client's
computer. They are transmitted via headers sent forth between the
client (in this case the browser) and the web server.
When the server tells the browser to set a cookie, the header might look something like this:
Set-cookie: has_been_here=yes
This tells the browser to save a cookie with the name has_been_here and the value of that cookie should be yes.
The full syntax of that header is:
Set-Cookie: <name>=<value>[;
<name>=<value>]... [; expires=<date>][;
domain=<domain_name>] [; path=<some_path>][; secure][;
httponly]
We will talk about the other options later. When a browser has
cookies for a specific page it will send them in a header like this:
Cookie: <name>=<value> [;<name>=<value>]...
In our above example it would then be:
Cookie: has_been_here=yes
So far so good... To make this a bit easier for us, PHP has some
functions that will help us in using cookies. An important function is setcookie(). This function enables us to set cookies with the options explained above for the Set-Cookie HTTP response header.
Because the setcookie() function is sending headers, it is important that you call this function before
any content has been output to the browser. Headers are always at the
top of an HTTP response, so once you start outputting content you will
not be able to send any more headers.
Setting cookies
The first two parameters of the setcookie() function ($name and $value) are obvious. They are setting the name and the value of the cookie, respectively. The other ones might not be so obvious. $expire is for how long the cookie should last. This is expressed with a Unix timestamp.
You will have to set the date and time of when the cookie should
expire. You will not be able to make a cookie that lasts "forever", but
you can make the cookie last a long time (several years for instance).
If you want to set a cookie that lasts 30 days, then you could get the
timestamp like this:
$expire = time() + 60 * 60 * 24 * 30; // or 2592000
// ... or ...
$expire = strtotime('+30 days');
Both
of those two things will get the current timestamp and add 30 days to
it. You can then pass that value to the third parameter.
The fourth parameter, $path, is used to control which
scripts that can access the cookie. This is relative to the document
root. Say for instance you have http://example.com/forums/index.php and
you want to set a cookie using that file. If you set the path to /forums,
then the cookie will only be accessible to scripts within the /forums
path. If you leave it blank then there is no restriction.
The fifth paramter, $domain, works much like $path
does. However, this time the restriction is which domains that can
access the cookie. If we say that you both have www.example.com and
forums.example.com then setting $domain to forums.example.com will mean that the cookie is only accessible from the forums subdomain. You can also set it to .example.com and then all subdomains for example.com (e.g. www and forums) will be able to access it.
If you are using a free host where your domain name will be a
subdomain of the host's domain (e.g. mysite.awesome-host.biz or
something like that) then you might want to ensure that only your
specific subdomain will be able to access your cookies so all the other
users won't eat them.
$domain defaults to the hostname from which it was set. This means that example.com will not be able to get example2.com's cookies.
The two last parameters are a bit more advanced. If you set $secure to true then you are telling the browser to only send the cookie back to the server if the connection is encrypted using SSL (i.e. on a secure connection). $httponly
is used to only transmit the cookie to the server, i.e. client-side
scripting languages like Javascript will not be able to use them. This
is a useful way of dealing with XSS.
Getting cookies
The cookies that are available will always be accessible using $_COOKIE. This variable holds an associative array of all cookies. To get the has_been_here cookie from earlier, you would access it using $_COOKIE['has_been_here']. $_COOKIE is a super-global so you will always be able to access it in PHP no matter where you are in the script.
When you set a cookie on a page it will not be immediately accessible through $_COOKIE, so don't try to use it.
An example of using cookies
Here is an example of a small script using cookies:
<?php
$visits = intval($_COOKIE['visits']);
setcookie('visits', $visits + 1, strtotime('+30 days'));
if ($visits == 0) {
echo 'I have never seen you before, but I am glad you are here :)';
}
else {
echo 'Welcome back! You have been here ', $visits, ' time(s) before';
}
?>
Try
to upload the script to your web server and see what happens. Then try
to refresh a couple of times and see the changes. We have now
successfully added a state to a stateless protocol.
On the next page we will look at another way of doing this through the usage of sessions.
Sessions
Another popular method is using sessions. Sessions
are very similar to cookies, but they are distinctive in a few
important ways. While cookies are stored on the client machine,
sessions are stored on the server.
The way sessions work are by generating a random ID for the user.
This ID is stored in a cookie on the client machine. PHP will then
fetch all session data related to that ID from wherever it is stored.
By default it's stored on the harddisk, but it's possible to write your own save handler so you can store it in e.g. a database. By default, the cookie will be called PHPSESSID and will expire when the browser is closed.
Seeing as you are using a cookie for identifying the user you'll
need a way to set the same settings for the cookie as we did before.
For this purpose you can use the session_set_cookie_params() function. It has the following syntax:
session_set_cookie_params(int $lifetime[, string $path[, string $domain[, bool $secure[, bool $httponly]]]])
Using session data
Before you are able to use sessions you'll have to call session_start(). This function will, like when setting cookies, have to be called before
any output has been sent to the browser. Once this has been done,
storing data in sessions and retrieving the values is quite easy:
$_SESSION['has_been_here'] = true;
Like $_COOKIE, $_SESSION is a super-global as well.
An example of using sessions
This is how the script we made using cookies on the previous page will look if we decide to use sessions instead:
<?php
session_start();
$visits = $_SESSION['visits']++;
if ($visits == 0) {
echo 'I have never seen you before, but I am glad you are here :)';
}
else {
echo 'Welcome back! You have been here ', $visits, ' time(s) before';
}
?>
Sessions without cookies
Sessions do not always have to use cookies. Whatever value you choose for the session name (PHPSESSID
by default) just have to be present somewhere. It could for instance be
through the URL like this:
http://example.com/index.php?PHPSESSID=jfJk87Hfja87Hajhsd97Hash or it
could be through a form field (either via POST or GET). The manual has
more information about this, but I would recommend you just use the
cookies.
On the next page we will talk a bit about security...
Security issues
Because the cookies are transferred
unencrypted (unless you are using SSL) and because they will be stored
in plaintext on the client computer, it is not advised that you store
any sensitive information in the cookies. Such information could, for
instance, be the username and/or password. For purposes such as
authentication it would be best to store a unique identifier in a
cookie and store the rest of the information on the server. In that way
it never leaves the server and therefore it is stored in a safe manner
(providing your server is secure). Seeing as sessions do this, they
would be a better choice for that purpose.
An instance where you would not want to use sessions instead of
cookies would be if you need Javascript or another client-side
scripting language to access the information. Seeing as session data is
stored on the server, there is no way whatsoever it can be access from
the client machine. Again, this information should not be sensitive.
A problem related to this is a security issue known as XSS (Cross
Site Scripting). This vulnerability exists if users are able to post
information (via a form or the URL for instance) and that information
is redisplayed on a page without being properly escaped. In that way a
malicious user will be able to execute any arbitrary Javascript code on
the client machine without the user or you knowing it. The reason why
this is a problem regarding cookie and session security is that cookies
will be default be able to be viewed by Javascript. There are a number
of different ways you could deal with this. The first and most obvious
one is to properly escape all output coming from a user. The function htmlentities() can take care of that. Another way is by setting the httponly
parameter for your cookies. In that way the browser will not give
Javascript access to the information that cookie holds. If you are
using sessions, then you can call session_regenerate_id()
on every request. By doing that the user will get a new session ID each
time and it means that if a malicious user ever gets a session id,
chances are it will be invalid by the time he gets to try it because it
changes often. You can use any combination of these three things
(although you should always do the first). Stealing the session
IDs and attempting to use them is known as session fixation. There are
plenty of resources available regarding both XSS and session fixation
available if you want further information regarding those two subjects.
Conclusion
That's all I have to say about cookies and
sessions right now. With this new information you will be able to
remember things about your users.