About the Author

Chris Shiflett

Hi, I’m Chris: web craftsman, community leader, husband, father, and partner at Fictive Kin.


The Truth about Sessions

  • Published in PHP Magazine on 15 Dec 2003
  • Last Updated 15 Dec 2003
  • 81 comments

Nearly every PHP application uses sessions. This article takes a detailed look at implementing a secure session management mechanism with PHP. Following a fundamental introduction to HTTP, the challenge of maintaining state, and the basic operation of cookies, I will step through simple and effective methods that can be used to increase the security and reliability of your stateful PHP applications.

It is a common misconception that PHP provides a certain level of security with its native session management features. On the contrary, PHP simply provides a convenient mechanism. It is up to the developer to provide the complete solution, and as you will see, there is no one solution that is best for everyone.

Statelessness

HTTP is a stateless protocol. This is because there is nothing within the protocol that requires the browser to identify itself during each request, and there is also no established connection between the browser and the web server that persists from one page to the next. When a user visits a web site, the user's browser sends an HTTP request to the web server, which in turn sends an HTTP response in reply. This is the extent of the communication, and it represents a complete HTTP transaction.

Because the web relies on HTTP for communication, maintaining state in a web application can be particularly challenging for developers. Cookies are an extension of HTTP that were introduced to help provide stateful HTTP transactions, but privacy concerns have prompted many users to disable support for cookies. State information can be passed in the URL, but accidental disclosure of this information poses serious security risks. In fact, the very nature of maintaining state requires that the client identify itself, yet the security-conscious among us know that we should never trust information sent by the client.

Despite all of this, there are elegant solutions to the problem of maintaining state. There is no perfect solution, of course, nor is there one solution that can satisfy everyone's needs. This article introduces some techniques that can reliably provide statefulness as well as defend against session-based attacks such as session hijacking. Along the way, you will learn how cookies really work, what PHP sessions do, and what is required to hijack a session.

HTTP Overview

In order to appreciate the challenge of maintaining state as well as choose the best solution for your needs, it is important to understand a little bit about the underlying architecture of the web, the Hypertext Transfer Protocol (HTTP).

A visit to http://example.org/ requires the web browser to send an HTTP request to example.org on port 80. The syntax of the request is something like the following:

GET / HTTP/1.1
Host: example.org

The first line is called the request line, and the second parameter (a slash in this example) is the path to the resource being requested. The slash represents the document root; the web server translates the document root to a specific path in the filesystem. Apache users might be familiar with setting this path with the DocumentRoot directive. If http://example.org/path/to/script.php is requested, the path to the resource given in the request is /path/to/script.php. If the document root is defined to be /usr/local/apache/htdocs, the complete path to the resource that the web server uses is /usr/local/apache/htdocs/path/to/script.php.

There are many technologies such as mod_rewrite that offer more flexibility when mapping a URL to a particular resource.

The second line illustrates the syntax of an HTTP header. The header in this example is Host, and it identifies the domain name of the host from which the browser intends to be requesting a resource. This header is required by HTTP/1.1 and helps to provide a mechanism to support virtual hosting, multiple domains being served by a single public IP address. There are many other optional headers that can be included in the request, and you may be familiar with referencing these in your PHP code; examples include $_SERVER['HTTP_REFERER'] to refer to the Referer header and $_SERVER['HTTP_USER_AGENT'] to refer to the User-Agent header.

Of particular note in this example request is that there is nothing within it that can be used to uniquely identify the client. Some developers resort to information gathered from TCP/IP (such as the IP address) for unique identification, but this approach has many problems. Most notably, a single user can potentially use a different IP address for each request (as is the case with large ISPs such as AOL), and multiple users can potentially use the same IP address (as is the case in many computer labs using an HTTP proxy). These situations can cause a single user to appear to be many, or many users to appear to be one. For any reliable and secure method of providing state, only information obtained from HTTP can be used.

The first step in maintaining state is to somehow uniquely identify each client. Because the only reliable information that can be used for such identification must come from the HTTP request, there needs to be something within the request that can be used for unique identification. There are a few ways to do this, but the solution designed to solve this particular problem is the cookie.

Cookies

The realization that there must be a method of uniquely identifying clients has resulted in cookies, a fairly creative solution. Cookies are easiest to understand if you consider them to be an extension of the HTTP protocol, which is precisely what they are. Cookies are defined by RFC 2965, although the original specification written by Netscape more closely resembles industry support.

There are two HTTP headers that are necessary to implement cookies, Set-Cookie and Cookie. A web server includes a Set-Cookie header in a response to request that the browser include this cookie in future requests. A compliant browser that has cookies enabled includes the Cookie header in all subsequent requests (that satisfy the conditions defined in the Set-Cookie header) until the cookie is expired. A typical scenario consists of two transactions (four HTTP messages):

  1. Client sends an HTTP request.
  2. Server sends an HTTP response that includes the Set-Cookie header.
  3. Client sends an HTTP request that includes the Cookie header.
  4. Server sends an HTTP response.

This exchange is illustrated in Figure 1.

Figure 1:

A Typical Cookie Exchange

The addition of the Cookie header in the client's second request provides information that the server can use to uniquely identify the client. It is also at this point that the server (or a server-side PHP script) can determine whether the user has cookies enabled. Although the user can choose to disable cookies, it is fairly safe to assume that the user's preference will not change while interacting with your application. This fact can prove to be very useful, as will soon be demonstrated.

GET and POST Data

There are two additional methods that a client can use to send data to a server, and these methods predate cookies. A client can include information in the URL being requested, whether in the query string or as part of the path. As an example of utilizing the query string, consider the following example request:

GET /index.php?foo=bar HTTP/1.1
Host: example.org

The receiving script, index.php, can reference $_GET['foo'] to reference the value bar. Because of this, most PHP developers refer to this data as GET data (others sometimes refer to it as query string data or URL variables). One common point of confusion is that GET data can exist in a POST request, because it is simply part of the URL being requested and doesn't rely on the request method.

Another method that a client can use to send information is by utilizing the content portion of an HTTP request. This technique requires that the request method be POST, and an example of such a request is as follows:

POST /index.php HTTP/1.1
Host: example.org
Content-Type: application/x-www-form-urlencoded 
Content-Length: 7
 
foo=bar

In this case, the receiving script, index.php, can reference $_POST['foo'] to reference the value bar. PHP developers typically refer to this data as POST data, and this is how a browser passes data submitted from a form where the method is POST.

A request can potentially have both types of data, like this:

POST /index.php?myget=foo HTTP/1.1
Host: example.org
Content-Type: application/x-www-form-urlencoded 
Content-Length: 11
 
mypost=bar

These two additional methods of sending data in a request can provide substitutes for cookies. Unlike cookies, GET and POST data support is not optional, so these methods can also be more reliable. Consider a unique identifier called PHPSESSID included in the request URL as follows:

GET /index.php?PHPSESSID=12345 HTTP/1.1
Host: example.org

This achieves the same goal as the Cookie header, because the client identifies itself, but it is much less automatic for the developer. Once a cookie is set, it is the browser's responsibility to return it in subsequent requests. To propagate the unique identifier through the URL, the developer must ensure that all links, form submission buttons, and the like contain the appropriate query string (PHP can help with this if you enable session.use_trans_sid). In addition, GET data is displayed in the URL and is much more exposed than a cookie. In fact, unsuspecting users might bookmark such a URL, send it to a friend, or do any number of things that can accidentally reveal the unique identifier.

Although POST data is less likely to be exposed, propagating the unique identifier as a POST variable requires all requests to be POST requests. This is not a convenient option, although your application design might make it more viable.

Session Management

Until now, I have been discussing state. This is a rather low-level detail that involves associating one HTTP transaction with another. The more useful feature that you are likely to be familiar with is session management. Session management not only relies on the ability to maintain state, but it also requires that you maintain data uniquely associated with each user. This data is often called session data, because it is associated with a specific user's session. If you use PHP's built-in session management mechanism, session data is persisted for you (in /tmp by default) and available in the $_SESSION superglobal. A simple example of using sessions involves the persistence of session data from one page to the next. Listing 1, start.php, demonstrates how this can be done.

Listing 1:

<?php
 
session_start();
$_SESSION['foo'] = 'bar';
 
?>
 
<a href="continue.php">continue.php</a>

Assuming the user clicks the link in start.php, the receiving script (continue.php) will be able to access the same session variable, $_SESSION['foo']. This is shown in Listing 2.

Listing 2:

<?php
 
session_start();
echo $_SESSION['foo']; /* bar */
 
?>

Serious security risks exist when you write code like this without understanding what PHP is doing for you. Without this knowledge, you will find it difficult to debug session errors or provide any reasonable level of security.

Impersonation

It is a common misconception that PHP's native session management mechanism provides safeguards against common session-based attacks. On the contrary, PHP simply provides a convenient mechanism. It is the developer's responsibility to provide the appropriate safeguards for security. As mentioned previously, there is no perfect solution, nor a best solution that is right for everyone.

To explain the risk of impersonation, consider the following series of events:

  1. Good Guy visits http://example.org/ and logs in.
  2. example.org sets a cookie, PHPSESSID=12345.
  3. Bad Guy visits http://example.org/ and presents a cookie, PHPSESSID=12345.
  4. example.org mistakes Bad Guy for Good Guy.

These events are illustrated in Figure 2.

Figure 2:

An Impersonation Attack

Of course, this scenario assumes that Bad Guy somehow discovers or guesses the valid PHPSESSID that belongs to Good Guy. While this may seem unlikely, it is an example of security through obscurity and is not something that should be relied upon. Obscurity isn't a bad thing, of course, and it can help, but there needs to be something more substantial in place that offers reliable protection against such an attack.

Preventing Impersonation

There are many techniques that can be used to complicate impersonation or other session-based attacks. The general approach is to make things as convenient as possible for your legitimate users and as complicated as possible for the attackers. This can be a very challenging balance to achieve, and the perfect balance largely depends on the application design. So, you are ultimately the best judge.

The simplest valid HTTP/1.1 request consists of a request line and the Host header:

GET / HTTP/1.1
Host: example.org

If the client is passing the session identifier as PHPSESSID, this can be passed in a Cookie header as follows:

GET / HTTP/1.1
Host: example.org
Cookie: PHPSESSID=12345

Alternatively, the client can pass the session identifier in the request URL:

GET /?PHPSESSID=12345 HTTP/1.1
Host: example.org

The session identifier can also be included as POST data, but this typically involves a less friendly user experience and is the least popular approach.

Because information gathered from TCP/IP cannot be reliably used to help strengthen the security of the mechanism, it seems that there is little that a web developer can do to complicate impersonation. After all, an attacker must only provide the same unique identifier that a legitimate user would in order to impersonate that user and hijack the session. Thus, it would appear that the only protection is to either keep the session identifier hidden or to make it difficult to guess (preferably both).

PHP generates a random session identifier that is practically impossible to guess, so this concern is already mitigated. Preventing the attacker from discovering a valid session identifier is much more difficult, because much of this responsibility lies outside of the developer's realm of control.

There are many situations that can result in the exposure of a user's session identifier. GET data can be mistakenly cached, observed by an onlooker, bookmarked, or emailed. Cookies provide a safer mechanism, but users can disable support for cookies, ruling out the possibility of using them, and past vulnerabilities in Internet Explorer have been known to reveal cookies to unauthorized sites.

Thus, a developer can be fairly certain that a session identifier cannot be guessed, but the possibility that it can be revealed to an attacker is more likely, regardless of the method used to propagate it. Something additional is needed to help prevent impersonation.

In practice, a typical HTTP request includes many optional headers in addition to Host. For example, consider the following request:

GET / HTTP/1.1
Host: example.org
Cookie: PHPSESSID=12345
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1
Accept: text/html;q=0.9, */*;q=0.1
Accept-Charset: ISO-8859-1, utf-8;q=0.66, *;q=0.66
Accept-Language: en

This example includes four optional headers, User-Agent, Accept, Accept-Charset, and Accept-Language. Because these headers are optional, it is not very wise to rely on their presence. However, if a user's browser does send these headers, is it safe to assume that they will be present in subsequent requests from the same browser? The answer is yes, with very few exceptions. Assuming that the previous example is a request sent from a current user with an active session, consider the following request sent shortly thereafter:

GET / HTTP/1.1
Host: example.org
Cookie: PHPSESSID=12345
User-Agent: Mozilla/5.0 (compatible; IE 6.0 Microsoft Windows XP)

Because the same unique identifier is being presented, the same PHP session will be accessed. If the browser is identifying itself differently than noted in previous interactions, should it be assumed that this is the same user?

It is hopefully clear that this is not desirable, yet this is exactly what happens if you do not write code that specifically checks for such situations. Even in cases where you cannot be sure that the request is an impersonation attack, simply prompting the user for a password can help prevent impersonation without adversely affecting your users too much. This is an important point.

You can add User-Agent checking to your security model with code similar to that Listing 3.

Listing 3:

<?php
 
session_start();
 
if (md5($_SERVER['HTTP_USER_AGENT']) != $_SESSION['HTTP_USER_AGENT']) {
   /* Prompt for Password */
   exit;
}
 
/* Rest of Code */
 
?>

Of course, you will need to first store the MD5 digest of the user agent whenever you first begin a session, as shown in Listing 4.

Listing 4:

<?php
 
session_start();
 
$_SESSION['HTTP_USER_AGENT'] = md5($_SERVER['HTTP_USER_AGENT']);
 
?>

While it is not necessary that you use the MD5 of User-Agent, it helps provide consistency and eliminates the necessity to filter $_SERVER['HTTP_USER_AGENT'] before using it. Because this data originates from a remote source, it should not be blindly trusted, but the format of an MD5 digest is consistent.

Now that you enforce User-Agent consistency, an attacker must complete two steps in order to hijack a session:

  1. Capture a valid session identifier.
  2. Present the victim's User-Agent header in the impersonation attempt.

While this is clearly possible, it is more slightly more complicated, therefore the session mechanism is already more secure.

Other headers can be added in this way, and you can even use a combination of headers as a fingerprint. If you also include some secret padding of some sort, this fingerprint becomes practically impossible to guess. Consider the example Listing 5.

Listing 5:

<?php
 
session_start();
 
$fingerprint = 'SHIFLETT' . $_SERVER['HTTP_USER_AGENT'];
$_SESSION['fingerprint'] = md5($fingerprint . session_id());
 
?>

The Accept header should not be used in the fingerprint, because some browsers vary the value of this header when the user refreshes the page.

With a fingerprint that is difficult to guess, little is gained without using it. Consider a session mechanism where the fingerprint is propagated just like the session identifier. In this case, an attacker must complete the following three steps to successfully hijack the session:

  1. Capture a valid session identifier.
  2. Present the same HTTP headers used to generate the fingerprint.
  3. Present the victim's fingerprint.

If both the session identifier and the fingerprint are propagated as GET data, it is possible that an attacker who can obtain one will also have access to the other. A safer approach is to utilize two different methods of propagation, GET data and cookies. Of course, this relies upon the user's preferences, but an extra level of protection can be offered to those who enable cookies. Thus, if an attacker obtains the unique identifier by way of a browser vulnerability, the fingerprint is still likely to be unknown.

There are many more techniques that can be used to help strengthen the security of your session mechanism. Hopefully you are well on your way to creating some techniques of your own. After all, you are the expert of your own applications, so armed with a good understanding of sessions, you are the best person to implement some additional security.

Obscurity

I would like to dispel a common myth about obscurity. The myth is that there is "no security through obscurity." As I mentioned earlier, obscurity is not something that offers adequate protection, nor should it be relied upon. However, this does not mean that there is absolutely nothing to be gained. Backed by a secure session mechanism, obscurity can offer a bit of additional security.

Simply using misleading variable names for the session identifier and fingerprint can help. You can also propagate decoy data to mislead a potential attacker. These techniques certainly should never be relied upon for protection, of course, but you will not waste your time by implementing a bit of obscurity in your own mechanism.

Summary

I hope that you have gained several things from this article. Notably, you should now have a basic understanding of how the web works, how statefulness is achieved, what a cookie really is, how PHP sessions work, and some techniques that you can use to improve the security of your sessions.

If you enjoyed this article, you might also be interested in these others:

If you develop a secure session mechanism of your own, please feel free to share it with the community. I would love to hear about your own solutions, and I hope this article provides the background information necessary to support your own creativity.

About this article

The Truth about Sessions was last updated on 15 Dec 2003. Follow me on Twitter.

81 comments

1.Jim wrote:

I enjoyed the article, nicely done :)

Tue, 16 Nov 2004 at 19:24:42 GMT Link


2.Hank wrote:

Very lucid summary of PHP security. Thanks.

Fri, 03 Dec 2004 at 01:03:55 GMT Link


3.Max wrote:

Great ideas. Thanks. I am a bit confused though. You state that "Obscurity shouldn't be relied upon," yet the two techniques presented here are themselves still effectively methods relying on obscurity - albeit a much greater degree of obscurity.

I'm not going to argue that the techniques you present are insecure in practical application. However, it seems that "obscurity" is all that we have to rely upon for security; the question is simply a matter of /how/ obscure. Or am I mistaken?

Wed, 08 Dec 2004 at 23:13:32 GMT Link


4.Chris Shiflett wrote:

Max, you're right, and that's a very astute observation.

The "real" security in this case should be that the session identifier is not known by the attacker. What I am providing are techniques that can make an attacker's job much harder, just in case the session identifier is known.

It's true that these techniques can be considered forms of obscurity. In fact, the use of a session identifier can be considered obscurity. Technically, most forms of protection can be considered nothing more than obscurity, by strict definition. This is because obscurity refers to how unknown something is.

Hopefully, the session identifier is very unknown, rather than just being very difficult to guess (which more adequately describes something like the User-Agent header). If there were a good method for measuring how unknown something is, it would provide a better indication of how strong a safeguard is.

This is all a big part of the reason that I don't believe in the "no security through obscurity" rhetoric. However, I also don't believe it in the spirit I think it is used - that safeguards can offer no protection if they are trivially obscure. That's why I included the section on obscurity at the end - I wanted to dispel the myth without confusing the issue with semantics, which is what I'm afraid I might have done now. :-)

Mon, 13 Dec 2004 at 03:41:54 GMT Link


5.Cd-MaN wrote:

A very good idea (the fingerprinting). I've had the same idea some time ago. However I would suggest to include the following fields in the hash:

HTTP_USER_AGENT

REMOTE_ADDR

HTTP_X_FORWARDED_FOR

and drop

HTTP_ACCEPT_CHARSET as it might change for legitimate reasons (I've had that and it took a little to debug!).

Tue, 01 Feb 2005 at 06:21:22 GMT Link


6.Gordon wrote:

$_SERVER ['REMOTE_ADDR'] is a good one to use as it contains the IP address the request came from, but consider AOL users. They are stuck behind a proxy firm that means that their IP address can change every couple of minutes. If you use REMOTE_ADDR to secure sessions then you'll find you get an awful lot of complaints from AOL users who find themselves logged out of your application every few minutes! Perhaps just using a portion of the IP address would be a better approach, but I'm not sure how many octets of the IP address get changed by the rotating proxies. And, of course, the less of the IP address you use, the easier you make it to bypass security.

Tue, 01 Feb 2005 at 18:39:33 GMT Link


7.Daniel wrote:

Hello!

I'm a student from Estonia and I have been developing PHP a year and half.

I have to thank the author of this article, because I found it quite easy to understand. I'm not a professional developer, so I appreciate the user-friendly material about programming-issues.

So far I have used only PHPSESSID url variable to identify my clients without cookie support, but great ideas introduced in the article made me consider more appropriate methods to achieve my goals.

Firstly, many aspects described definitely need the attention, and I'm going to handle my user authentication information according to some of them.

Secondly, as a result of increasing security threats, more reliable solutions have to be used to improve the reliability of the code and applications.

Finally I would like to wish good luck to the authors of this site, because I found something really interesting from here! :)

Well done!

Tue, 01 Feb 2005 at 23:33:43 GMT Link


8.Lokela wrote:

In response to Gordon, you can find out the class of the remote IP address (A, B, C) and common subnet mask. I don't _believe_ that AOL (or any other) proxy servers will ever change subnets.

http://www.j51.com/~sshay/tcpip/ip/ip.htm

Wed, 02 Feb 2005 at 23:53:55 GMT Link


9.Titeuf wrote:

This is a very interesting article that i found on a very interesting website about php security:

http://phpsec.org/

So, in this article one of the solution consist in use of the HTTP HEADER to compare two requests with the same sess. id. Well, it could be very interesting but when i look my stats i see that 11.74% of my members use the same browser (the same header user agent i guess)...so i thing it's a very high score that authorize me to guess that hacker could use too the same header... Ok probably, there is more than 90% that the hacker use another agent ;-D

Thanks again about this article...hope more solution against hijacking will be explain...

Sun, 06 Feb 2005 at 19:15:35 GMT Link


10.Beoran wrote:

I would say that even though this technique helps a bit, it's no replacement for encryption. If security really matters, we should use HTTPS.

Tue, 08 Feb 2005 at 15:35:52 GMT Link


11.Chris Shiflett wrote:

No safeguard is ever a replacement for any other safeguard. That is never the point, plus it violates the principle of defense in depth.

Anything you do that "helps a bit" increases the security of your application, even if the increase is slight.

Tue, 08 Feb 2005 at 15:59:35 GMT Link


12.BayK wrote:

I'm currently writing a paper on session management and transactions and your articles are fountains of knowledge ;)

awesome :top:

Tue, 08 Feb 2005 at 16:29:02 GMT Link


13.Chris wrote:

@Lokela

It is very possible in complex cache hierarchies for a client to appear to come from a couple different subnets if. I have seen it only with one user one time...

Tue, 08 Feb 2005 at 18:21:51 GMT Link


14.mjec wrote:

The method you describe for browser fingerprinting seems ineffectual. If an attacker has intercepted the session ID then they will no doubt have the User-Agent and Accept headers. One can always guess, too, that more than half of all UA strings match IE6 ;).

Far more effective is the use of remote addresses, combind with Via and X-Forwarded-For headers. I also use session polymorphism - the session ID changes on each page load, invisible to the end user, far more difficult to the attacker.

Really though, the only effective mechanism to use is HTTPS. If you're that concerned, get yourself a cert. Messing about with fingerprinting offers very little additional security and can (as Cd-MaN and Gordon point out) have a usability detriment.

Fri, 11 Feb 2005 at 07:41:40 GMT Link


15.Chris Shiflett wrote:

Cd-MaN writes:

A very good idea (the fingerprinting). I've had the same idea some time ago. However I would suggest to include the following fields in the hash: HTTP_USER_AGENT

This is included in the article.

REMOTE_ADDR

It is a bad idea to rely upon the IP address.

drop HTTP_ACCEPT_CHARSET as it might change for legitimate reasons (I've had that and it took a little to debug!).

Good point. Accept should also not be used, for the same reason.

Gordon writes:

$_SERVER ['REMOTE_ADDR'] is a good one to use as it contains the IP address the request came from, but consider AOL users...

AOL is not the only large ISP. They just happen to be the most common example. You see, when inexperienced developers suggest using the IP address, experienced developers need a way to quickly point out what a terrible idea it is. It's usually enough to remind them that their site is now broken for several million AOL users. However, there are many, many more people who would be affected by such a broken implementation.

Lokela writes:

In response to Gordon, you can find out the class of the remote IP address (A, B, C) and common subnet mask. I don't _believe_ that AOL (or any other) proxy servers will ever change subnets.

They do. More important than this is the fact that you're trying to second guess a characteristic of a system that has absolutely nothing to do with the application layer. This can only end in tears. :-)

mjec writes:

The method you describe for browser fingerprinting seems ineffectual. If an attacker has intercepted the session ID then they will no doubt have the User-Agent and Accept headers.

You might want to read the article again. Knowing the User-Agent header is not the same as knowing the browser fingerprint. You're right that it's one more piece of the puzzle that we don't want the attacker to get.

Far more effective is the use of remote addresses, combind with Via and X-Forwarded-For headers.

This is a bad idea, as mentioned above. All the security in the world is pointless if your application is broken.

I also use session polymorphism - the session ID changes on each pageload, invisible to the end user, far more difficult to the attacker.

This is overkill. You only need to regenerate the session identifier whenever there is a change in privilege level. This helps to prevent session fixation (which is not the topic of this article). Regenerating the session identifier on every page load offers no additional protection and can adversely affect legitimate users who make use of the browser's history mechanism. I explain this in more detail in another article:

http://shiflett.org/articles/session-fixation

Really though, the only effective mechanism to use is HTTPS.

Using SSL can certainly help protect the session identifier, but it does not offer a flawless solution. In fact, this is the point of this article as well as the point of the principle of defense in depth.

Messing about with fingerprinting offers very little additional security and can (as Cd-MaN and Gordon point out) have a usability detriment.

There should be no usability detriment, because it is transparent to the user.

Lastly, realize that my job is not to convince you of anything. I offer my opinions to those who want to listen.

Fri, 11 Feb 2005 at 16:45:33 GMT Link


16.usugarbage wrote:

I don't understand all the barking at the author. The title of this page is the truth about SESSIONS. It was fairly concise and has offered a nice stepping point for readers to idea-up their own security scheme or fingerprinting technique. I've found that this article has offered a more solid solution than the dozens I've read in the last week.

No one is arguing that SSL is bad, its just not directly related to this topic. Thanks for the article Chris!

Sun, 13 Feb 2005 at 19:15:46 GMT Link


17.Cole wrote:

Thanks for the heads up. I've been developing PHP profesionally for about 1/2 a year now. This article will help out greatly with developing more secure apps for my clients!

Mon, 14 Feb 2005 at 21:56:22 GMT Link


18.Vale wrote:

Actually you did use Accept-Charset ;)

The article is great, thanks.

Thu, 17 Feb 2005 at 10:23:55 GMT Link


19.Roman wrote:

But he says after the listing:

The Accept header should not be used in the fingerprint, because...

And concerning the IP-Address and subnet problems. Maybe it's okay to use only the first 3 chiffres of the IP-ADDRESS. I guess there is no ISP which have more than one or even if one does, i don't think it give his users IP-Addresses from different class-A nets.

Fri, 18 Feb 2005 at 14:49:39 GMT Link


20.Garry wrote:

Reading all the above debates/information with great interest. Well done to the author. So, what about session_register? How bad is it to use one?

Mon, 21 Feb 2005 at 11:16:51 GMT Link


21.FerRory wrote:

My English is not so good. But how can I recognise the user id. Is my function secure or is a better way to secure the user id.

<?php
 
session_start();
 
function createSecureSession ()
{
     
    $fingerprint = 'SECRETSTUFF' . 
               $_SERVER['HTTP_USER_AGENT'] . 
               $_SERVER['HTTP_ACCEPT_CHARSET']; 
    $_SESSION['fingerprint'] = md5($fingerprint . session_id());
 
    $_SESSION[$_SESSION['fingerprint']] = 1;
}
 
createSecureSession ();
 
echo $_SESSION[$_SESSION['fingerprint']];
 
?>

Tue, 01 Mar 2005 at 15:40:36 GMT Link


22.Rory wrote:

I have it working now.

Chris how the hell, do you want to control if your fingerprint match the good guy and not the bad guy.

Mon, 07 Mar 2005 at 09:53:11 GMT Link


23.Chris Shiflett wrote:

Vale writes:

Actually you did use Accept-Charset.

This has been corrected. Thanks. :-)

Mon, 14 Mar 2005 at 21:12:36 GMT Link


24.Feran wrote:

By using GET and Cookies I'm assuming you mean to create cookies with the unique identifier and whatnot if the user has cookies enabled, and if the cookies are not enabled, use the GET?

Tue, 19 Apr 2005 at 05:54:41 GMT Link


25.Matt wrote:

Great article, thanks.

The background information was also appreciated, but I'm wondering now that people brought up SSL - What's to stop someone from emailing a link like: https://www.example.com/?PHPSESID=1234? Doesn't that have the same affect sans SSL?

Sat, 14 May 2005 at 20:48:03 GMT Link


26.Chris Shiflett wrote:

Matt, you're right. The reason people are bringing up SSL is to help protect against session hijacking, not session fixation. SSL helps to minimize the exposure of the session identifier.

Stated differently, protecting against session fixation helps prevent an attacker from using fixation to obtain a valid session identifier. It does not help you prevent an attacker from capturing one.

Mon, 16 May 2005 at 15:27:44 GMT Link


27.James Benson wrote:

Excellent article, 10/10,

thanks

Sat, 04 Jun 2005 at 03:31:57 GMT Link


28.uFiS wrote:

Just what I needed to get me going on the road to a more secure webapp.

Thanks

Mon, 18 Jul 2005 at 21:58:05 GMT Link


29.Cyrus wrote:

Chris, great article it brings up some very interesting points. Just a thought on using the http_user_agent. I work in a school district where we image all of our computers with the same software image (this is not a unique situation most schools and large offices do this). I've tried about ten computers now and each one returns with the exact same http_user_agent, which is to be expected as each computer has identical software. In this situation does this not negate the added security of using http_user_agent? As the MD5 sum will be the same for all clients, unless the secret stuff varies with each login?

Wed, 10 Aug 2005 at 15:05:37 GMT Link


30.Mary Sholl wrote:

Chris, great article

Sun, 11 Sep 2005 at 12:10:17 GMT Link


31.Alex Shelt wrote:

Thanks for the heads up. I've been developing PHP professionally for about 1/2 a year now. This article will help out greatly with developing more secure apps for my clients!

Mon, 12 Sep 2005 at 19:40:03 GMT Link


32.Alex Shelt wrote:

Thanks for the heads up. I've been developing PHP profesionally for about 1/2 a year now. This article will help out greatly with developing more secure apps for my clients!

Mon, 12 Sep 2005 at 19:40:05 GMT Link


33.Jeremy Chin wrote:

Chris,

Give a man a fish and he'll eat for a day. Teach a man how to fish and he'll eat for a lifetime. I definitely think your article belongs in the latter as you did a marvelous job of explaining the mechanics of how servers handle requests, and how security holes can be, and are, exploited.

Your article serves as a good foundation builder, and while no solution is fool proof, it is better to lock three of your car doors than to leave all four open with the key in the ignition.

Sun, 09 Oct 2005 at 11:13:15 GMT Link


34.Chris Shiflett wrote:

Jeremy, thanks for the kind words, and your analogy is right on target - a step in the right direction is always a good thing. In the case of session hijacking, the protection of the session identifier is paramount, but that doesn't mean other safeguards are without merit.

I also love your "teach a man to fish" analogy, because that's a big part of my writing style. I believe that if I can explain a problem thoroughly enough, more people will understand it. Once a problem is widely understood, a better solution is more likely to be discovered.

Tue, 11 Oct 2005 at 01:48:47 GMT Link


35.ITWow wrote:

Chris, great article.

Just picking up on a point made by Cyrus. I too work in a school environment where all computers are created from the one image. This gives every computer the same http_user_agent, etc. What else could be used to circumvent this?

A1 article!! Keep up the excellent work.

Tue, 11 Oct 2005 at 18:36:56 GMT Link


36.Richard wrote:

I didn't know about AOL (and others) changing the IP address mid-session - news just in time as I've partially implemented checking that to validate the session ID. I'll abandon that. Thanks!

I also regenerate the session ID number with a relative short expiration limit to allow a session to continue to be alive as long as its active. I was unaware of the problems with a user using browser history on that. So to abandon that I will need to track the last page access for each session and kill the session if it has been too long. I presume that also means I would destroy all session variables at that point as well as the existing cookie and re-build a clean slate. (I share this in the hope that someone will comment if I apparently do not understand and am headed in the wrong direction.)

Thanks for the good article.

Tue, 08 Nov 2005 at 20:05:43 GMT Link


37.Alderete wrote:

Chris, nice article. I did end up with a couple of areas where I thought a concept was unclear, or I wished you'd added a bit more.

First, you used two terms at different points in the article, without making it (crystal) clear that they meant the same thing. That is, I think you should be explicit that an "impersonation attack" and "session hijacking" are the same thing (getting ahold of the hard-to-guess session identifier).

Second, you use the term "session fixation" only in the comments, and contrast it with session hijacking, but I don't think it's adequately explained what session fixation means (you do note that it's not the topic of this article, though).

Am I correct in believing that session fixation is the problem of a session identifier remaining the same for too long? I.e., if the identifier is fixed, it makes it more likely that at some point it will be revealed, allowing the session to be hijacked?

Thanks!

Tue, 31 Jan 2006 at 03:05:57 GMT Link


38.Chris Shiflett wrote:

Hi Alderete,

You're right about the use of the term impersonation in this article. I think session hijacking is the more common term, but I wanted to make this article more accessible, so I chose to describe it as impersonation. These are subtly different, actually, because impersonating someone suggests that you assume their identity. If you hijack the session of someone who is anonymous and hasn't yet authenticated, you're not really impersonating them.

Session fixation has been brought up in the comments, and I only responded in order to point out that it's a related but entirely different attack. I wrote an article specifically on session fixation:

http://shiflett.org/articles/session-fixation

Hopefully that can help clear up any confusion.

Thanks for reading! :-)

Tue, 31 Jan 2006 at 15:33:57 GMT Link


39.cw wrote:

Chris,

I hope you get paid well for your knowledge!

Regards to fingerprint, you suggest using SHIFLETT, which I assume is something else you would pick up from the header.

You warn away from using Accept headers, and some of the other comments note REMOTE_ADDR is also a poor choice. So I am left wondering what should I use?

I've considered using $fingerprint = strrev($_SESSION['HTTP_USER_AGENT'])

good/bad idea?

Thanks

Fri, 03 Feb 2006 at 15:46:50 GMT Link


40.Octavio wrote:

Hi, this is a great article I've never found such a good and simple article on php session security. I would like to ask if there is a way to avoid of two users to log in at the same time with the same login and password, I ask this because session id is different with each login, so if someone guess the password of another user and login while the "real" user is logged there is no way to identify wich one is the real user . people say that I could save the ip address or the session id in a database and check against that data each time a user login, but I think that is not good because when a user log out by simply closing the browser and not via a log out link you can't save info to the database because the event of closing a browser window is the same event of leaving the page to another url by clicking a url (speaking in terms of javascript to trigger a php script when the user closes the browser window)

I hope you understand what Im trying to ask :D im not from an english spoken country (colombia), so Im sorry if I made a mistake in my writing

thanks

Tue, 28 Mar 2006 at 14:20:44 GMT Link


41.headless_migraine wrote:

hey.. thanks for the excellent article. I have a question, though. If AOL / other_big_isp changes remote_ip and other header information with each request, how do we go about creating a fingerprint ?

Mon, 10 Apr 2006 at 02:37:14 GMT Link


42.Bert wrote:

Most of your security mechanisms rely on the fact that an attacker can't modify HTTP headers. Why? If he had the ability to sniff the session ID in the first place, it seems to me that an attacker can simply spoof the HTTP headers of the original request just to be sure it looks like it's coming from the Good Guy instead of the Bad.

Thu, 01 Jun 2006 at 05:02:14 GMT Link


43.Chris Shiflett wrote:

Hi Bert,

I think you've misunderstood something. Techniques like this exist because it's trivial to modify HTTP headers. In fact, I'd word that differently - an attacker can send anything. (It doesn't even have to be valid HTTP, but that's another topic.)

Imagine a locked door with a keypad. If you enter 192837465918273645, the door opens. Which is the primary risk?

1. An attacker knows the code.

2. An attacker can enter a code.

Articles like this assume the second item to be true, because the web application security focuses on public web applications. The point is to try to make the first item as unlikely as possible, which isn't always easy.

Hope that helps.

Thu, 01 Jun 2006 at 12:45:45 GMT Link


44.John Steele wrote:

Hi Chris,

First off, I'd like to say that I enjoy your articles, and I'm sure you've helped many PHP programmers along the way.

Unfortunately this isn't very good security advice. Using HTTP headers (which you give a very good short description of) is in *no* way secure. I know this wasn't the point of your article, but thought it relevant to some of the comments responding to it.

Your last entry is the difference between something you know and something you have.

Without a secure hardware device (something you have), the best that can be achieved with PHP's sessions is to issue a not-easily-guessed session id with a randomly-generated encrypted session key, hopefully including a time-based token. This token should expire within a resonably short period of time, and have *nothing* to do with HTTP headers which are easily faked. Think uniqueid, microtime, md5/sha/des, etc.

I develop eCommerce sites, and if you took over say someone's cart, the worst they could do is change your cart contents. But in the checkout process, none of this is reasonable.

This is exactly what SSL was designed for, and even if you're savy enough to play a MITM (man-in-the-middle) attack, without the username, password, session id, and time-based token you would find it *very* hard to hijack the session.

PHP's session management is exactly what you said in your article, a simplified way to attempt to add state to a stateless protocol. Security is a whole different beast unfortunately.

Keep up the great articles,

John Steele - Systems Analyst / Programmer

Tue, 25 Jul 2006 at 08:18:25 GMT Link


45.Jefferson Campos wrote:

Hello!

Very good article, thanks!

Thu, 27 Jul 2006 at 14:19:51 GMT Link


46.wesley wrote:

You could use

HTTP_USER_AGENT

HTTP_ACCEPT_ENCODING

SERVER_PROTOCOL

Sat, 12 Aug 2006 at 08:36:43 GMT Link


47.LightDeveloper wrote:

Very clear article. Much better than some books. Security aspects in PHP session management must be considered always.

Sat, 12 Aug 2006 at 08:45:47 GMT Link


48.Bender wrote:

Well explained article Chris. Thanks a bunch. I understood it (mostly) even while not being a coder.

Also kinda frustrating but thats not your fault - lol.

Wed, 23 Aug 2006 at 20:05:34 GMT Link


49.Sarteck wrote:

Chris-

I'm fairly computer literate, but only a novice when it comes to Internet security. The way you set this article up makes even a "newbie" like me able to easily understand the concepts, and I'm especially grateful after reading two other articles on the same thing that seemed as if they were written in Greek.

Really, thanks. Not only have you set me on the track to making my websites more secure as a developer, but you've also made me more wary of my bookmarking and cookie cleaning as a user.

Seriously, before reading this article I had no idea whatsoever how $_SESSION variables were stored, or that they were even related to cookies. I thought it was a convenient, safe mechanism to use for automagically keeping information stored throughout a user's browsing of a site.

Thumbs up, man. Thanks for teaching a newbie. :3

-Sarteck

Thu, 31 Aug 2006 at 04:45:49 GMT Link


50.Scross wrote:

This a good tutorial and clearly demonstrates the risks of session hijacking. In my own application I have decided to create a low session expiration time of 10 minutes, but this is customizable by the user. I have also decided to implement a special mode which the user can enable or disable that requests a password for every "write" action.

So a hacker can read all of the data that the user can read (except passwords, obviously, which are md5'ed and hidden) but he/she can't change anything without the password. For further security a script could automatically change user's passwords every month and send them an email with the new password, but it is unlikely I will implement this because my script is not supposed to require mail() to work.

Thanks for your tutorial anyway Chris, really opened my eyes :)

Sun, 03 Sep 2006 at 10:27:39 GMT Link


51.Seb wrote:

Thank you, Chris! Your article could be compared as the seed that has now to grow up in developer's minds. A handful of ideas already took place in mine since I've read this topic, and I guess this is not the end.

I can only recommend to everyone to read it again a couple of times before making a comment, as many people seem not to have understood everything...

It is with guys like you that the internet stays open!

Mon, 11 Sep 2006 at 08:37:19 GMT Link


52.Jude wrote:

Couldn't we use

$_SERVER('HTTP_REFERER')

to help make sure that our user is really coming from a particular web page?

I mean, if I have a form on page X that is to be posted on page Y, this script in page Y could verify in a simple and secure manner that the information posted is coming only from page X and not, for example, page_evil.

Mon, 18 Sep 2006 at 07:16:31 GMT Link


53.Chris Shiflett wrote:

Please see this post:

http://shiflett.org/archive/96

Mon, 18 Sep 2006 at 07:41:04 GMT Link


54.Oli wrote:

Chris,

In Listing 5, you include some "secret" padding into the fingerprint; with the rationale being "this fingerprint becomes practically impossible to guess".

How so? If the fingerprint stays server side (for $_SESSION["fingerprint"] == $fingerprint comparisons only), the inclusion of padding in no way affects what the hacker has to do in order to impersonate a valid user.

The only way in which it could be useful is in the scenario you go on to describe, where the SID is sent via cookie, and the fingerprint via the GET string. But in this scenario, what is the benefit of using a fingerprint as the GET "ID" as opposed to a second random SID? Both have the property that if the hacker doesn't have access to it, then they can't guess it.

Sun, 24 Sep 2006 at 18:35:41 GMT Link


55.Chris Shiflett wrote:

Hi Oli,

To be honest, I rarely tie the fingerprint to the User-Agent. In that case, as you noted, the padding is important. Otherwise, an attacker could guess a valid token.

If you just use a random string, which is the approach I use most often, this risk doesn't exist.

In other words, I think you're right. :-)

Sun, 24 Sep 2006 at 18:43:46 GMT Link


56.Chris Shiflett wrote:

I want to stress that if the token is based on information that an attacker might conceivably predict (such as User-Agent), the secret padding is essential.

Sun, 24 Sep 2006 at 19:30:09 GMT Link


57.Wes Mahler wrote:

Great Article Again,

I also purchased your book!

Tue, 12 Dec 2006 at 19:00:54 GMT Link


58.Wes Mahler wrote:

Is it bad if I carry passwords in sessions during a user's visit?

Wed, 13 Dec 2006 at 14:56:51 GMT Link


59.Etienne Kneuss wrote:

Hi,

Salting $_SESSION['fingerprint'] doesn't make sense:

1) It's stored serverside and can't be accessed.

2) It doesn't contain anything sensitive.

3) Nobody want to reverse that.

In overall: it's much simpler to detect and replicate an http request than storing session files, reverse that md5 hash and guess the headers.

There is nothing hard about reproducing an http request, especially because session ids are often stolen using a redirection by XSS. The script that records session ids can easily record headers too.

Wed, 27 Dec 2006 at 20:05:51 GMT Link


60.Joseph Crawford wrote:

Chris,

This was a great article. Thanks for writing this, it really gave me some more expansion on the conversation we had in the past about session security.

I am going to take my session handler that i have and start integrating some of the stuff that i have gained from reading this article. I will also be posting an article on my blog outlining the code, explaining what i am doing where and why. I am only going to post the article on my site because my method of session security differs from yours above. Well it will be adding on to yours with my methods to add a bit more security. I will be sure to give you credit and link back to this article.

Thanks Again,

Joseph

Sat, 30 Dec 2006 at 02:30:31 GMT Link


61.Joseph Crawford wrote:

Chris writes:

Lastly, realize that my job is not to convince you of anything. I offer my opinions to those who want to listen.

Well said Chris ;)

Sat, 30 Dec 2006 at 02:33:38 GMT Link


62.Joseph Crawford wrote:

Chris,

One question that i have for you regarding the sessions is this. You state that if a browser sends an HTTP header for the most part you can assume it will send it again. You also get into talking about computer labs which have multiple computers routing through the same proxy server.

The question is this. Most computer labs will setup all the machines with the same setup so that when they need to run updates it is easier to do. If all of the machines are setup the exact same way, they will all report the same headers and use the same browsers, is there no way to differentiate between these users? Obviously you could set a cookie that one machine could have and another would not but the session could still be hijacked by one user grabbing the cookie from another machine.

With the help of Javascript could you retrieve the machines local name and maybe have that be returned for use in the fingerprint?

Thanks,

Joseph Crawford

Mon, 08 Jan 2007 at 22:51:05 GMT Link


63.Jay wrote:

RE: the comment's about the school networking. If your site requires a login name, an email address, etc that users are logging in with time after time you could always use it as part of your 'secret' padding as well. That would keep the fingerprint unique to each user/browser combination.

Thoughts?

Mon, 12 Mar 2007 at 17:01:13 GMT Link


64.Leo wrote:

I found this really interesting and informative, thanks.

Mon, 26 Nov 2007 at 09:45:17 GMT Link


65.Lewyx wrote:

I'm against using anything of the HTTP headers nowadays. Client IPs change. Busy user potentially change IP more frequently.

Example customer (say, Bill [I actually met all the components below, just not all of them in one customer ... no diff]): Bill uses a PDA. 3G blesses him with one of the cell-phone-comany's IPs. But when entering his company, wifi speeds him up with a cable-provider's IP. (IP changed). And while he answers all the questions of the collegues on the way to his office (playing the company portal on his PDA) Bill finally reaches his office. Log on to his workstation authenticates him, and his PDA to the company, reassigning the latter an insider IP of the inner company network, which happens to be VPN, accessing the internet at the company HQ's server park (possibly not on the same continent) [pretti big IP change]. And the shock: Bill puts the PDA into its cradle, which synchronizes browser workflows: opens the pages on the PDA on his workstation, so to be able to finish the half-done report on a convenient keyboard. Won't he be able just because browser type and capabilities, OS and compatibility have been changed? He'd be angry if he had retyped his password 5 times while just getting from the metro to his workstation.

This a problem with HTTP. It's so outdated that you cannot trust the data it presents. At least not expect it to be more as specified. UserAgent describes what the user uses not who the user is. Opera can change its identification even mid-request (page with the old ID, some of the pics with the ID you set while it loads the page). Proxies may legitimly modify lots of headers while accidentally even more.

Don't try to tell by the ink whether the signature is authentic! ;)

So the fingerprint is a nice technique I just don't think to find reliable data to be encapsulated into it.

Btw. I hate using GET variables. Freaks me out that there are still software the do not handle long request strings (e.g. famous proxies rejecting over a limit of 1024 bytes).

I hope I've been inspiring. Thanks to Chris for the perfect article!

Mon, 21 Jan 2008 at 02:37:12 GMT Link


66.Lee wrote:

Excellent article, demonstrating the weaknesses of relying on a single method of security. The defense in depth model should always be used, the more layers you put between the attacker and the data, the more likely you are to prevent or detect attacks.

Fri, 18 Apr 2008 at 15:31:26 GMT Link


67.dogglebones wrote:

hello.

php's built-in session_x() functions work well enough, but there are cases where you will want to roll your own. the first to come to mind is that you'll run into trouble when implementing a single site (domain) across multiple machines (httpd processes) as in a web server farm.

fortunately, php's header() or setcookie() functions make this an easy task.

i have monkeyed up a function called session_begin() which takes two optional arguments: session_id, and session_key.

session_id is, of course, a string which identifies a given unique session; session_key is a string which is intended to help ensure that the provided session is indeed unique. consider the following.

http client one comes along and acquires a legitimate session, abc123. along with generating this session, the server records something unique about http client one, or about the communication itself, such as http client one's remote ip address (not a good idea, keep reading). this can be anything at all, not necessarily limited to an http context, and this is stored as session_key. all subsequent requests from http client one will contain the session abc123. the idea is that session_key should be something determined by the server, on the server-side, not provided by the client (i.e., javascript or hidden form-field or whathaveyou), as this is trivial for an attacker to spoof.

now, sitting across the coffee shop, http client two has been watching http client one's tcp packets and knows exactly what that session_id is. therefore, http client two does not need to poke around for usernames or passwords. no need for a phishing site. no trojan horse, spyware, keystroke logger, fraudulent phone calls, or anything else. http client two needs only to make a request to the same domain with the same session_id, while http client one is logged in. http client two now has immediate access to anything and everything available to http client one, and nobody even knows about it. that's right, a simple, untraceable attack. this works, by my testing, with yahoo! mail, gmail, hotmail, and probably about every web site i could try it on, though ssl makes things more cumbersome - i should say, not very much more cumbersome, if the attacker can see the underlying tcp.

...however, since the server is also using session_key to store and retrieve it's sessions, http client two must appear to the server as identical to http client one. for example, say the server stores http_client_id ("mozilla" or "ie" or whathaveyou) in the session_key. now, http client two must make it's request to the server not only with the same session as http client one, but also with the same browsing software. or, say, session_key is remote_addr: now http client two must also spoof his victim's ip.

the obvious argument to this is that, in our scenario, http client two sees the whole http request made by his victim. therefore, it is entirely probable that he will spoof his request to match his victims to the greatest extent possible, in trying to make his hijack. therefore, the server would see the same http_client_id (or accept_charset or whatever else you could think of). and using the client-provided ip address or any function of the underlying ip connection as a session_key would be stupid on the server's part since the server may not know about http tunnels, proxy servers, ip-masquerading firewalls, or anything else which could (and inevitably would) obfuscate the reliability of such a method.

so, here is my post in a nutshell: what would serve as a good spoof-proof session_key? that is, what can a web server (or the system running a web server) see about a web client that is not likely (even better, not possible) to be reproduced in an attacker's hijack request? answer that, and you'll have a secure, unbreakable session management implementation.

it's not quite a rhetorical question, though i don't expect anybody to be able to answer, or else yahoo! and hotmail, gmail at least, would not be quite so easy to break into. if you can answer, though, you would be well-advised to seek a patent.

Fri, 31 Oct 2008 at 05:13:38 GMT Link


68.Nigel Peck wrote:

Hi Chris,

Thank you for writing this article, it has been very useful to me in rolling my own session management in Perl.

I just wanted to share my addition to your suggestions, as you encourage, in case it is useful to anyone else.

I am using sessions within my framework that I use to build website and other admin systems. A valid username and password is required to open a new session, which can then last for either the duration of the browser session, or expire in a months time. That's the background.

I am using a session_id cookie and MD5 hash of the user agent string, as you suggest. Extra to this, I also store a second cookie with the user_id from my users table. This is no use to anyone trying to compromise security, as it's the username that must be known to log on, but it's extra information that would have to be replicated to compromise the system by creating a fake session in cookies.

That's about it, hope this is useful to someone.

Cheers,

Nigel

Fri, 27 Feb 2009 at 20:50:54 GMT Link


69.Jaime Teixeira wrote:

This is a very good explanation about http, and maintaining state in the web.

Your explanations are well written.

Five stars!

Wed, 17 Jun 2009 at 23:36:12 GMT Link


70.Mark wrote:

After reading your article and all the comments, what I got out of this was that sessions are not secure and there ain't much we can do about it. That's kind of sad :(

I'm choosing to store my session ids in a cookie, and leaving it at that. I think the inconvenience to legitamate users outweighs the security benefits of trying to rely on http headers.

Thu, 25 Jun 2009 at 23:07:17 GMT Link


71.Konstantin wrote:

So can anyone explain to why/how the HTTP_ACCEPT_CHARSET header would legitimately change during a session?

Sat, 01 Aug 2009 at 06:05:50 GMT Link


72.mh wrote:

In your article, you mix up where the session data is stored. Your code examples suggest that it was stored server side. Your text suggests that it was stored client side. Your write:

an attacker must complete the following three steps to successfully hijack the session: 1. Capture a valid session identifier. 2. Present the same HTTP headers used to generate the fingerprint. 3. Present the victim's fingerprint.

If the fingerprint was stored client-side, this would render the whole procedure useless.

Bad Guy could do the following: 1. Request a page, getting his own session identifier and fingerprint 2. Capture a valid session identifier from Good Guy 3. Present the server a) Good Guy's session identifier b) Bad Guy's HTTP headers c) Bad Guy's fingerprint.

According to your article that would be accepted.

The fingerprint should only be stored on server side. And then we don't need salt or "misleading variable names". In fact, not even a hash.

Sun, 03 Jan 2010 at 11:53:45 GMT Link


73.Chris Shiflett wrote:

mh wrote:

In your article, you mix up where the session data is stored.

No, but it seems you are.

The point of having a fingerprint in addition to the session identifier is not just a means of forcing the attacker to reproduce the HTTP headers, although that is somewhat helpful. Over the years, most attacks that have been used to steal cookies also allow access to the HTTP headers, because they involve the victim visiting the attacker's site. Having something else makes session hijacking more difficult.

This article needs updating, because I prefer other techniques now (trending, for example), but the theory is sound, and it teaches valuable lessons.

Hope that helps.

Sun, 03 Jan 2010 at 15:35:09 GMT Link


74.mh wrote:

No, but it seems you are.

Okay, then please help me to understand you right. I made several points that you might want to comment on.

Let's start with my first point, storage of session data. You suggest using $_SESSION['fingerprint'] = md5($fingerprint . session_id()); and your article assumes, this data is stored on the client's side. I'd say, this assumption is wrong. The fingerprint data is stored on the server.

Mon, 11 Jan 2010 at 06:34:58 GMT Link


75.Chris Shiflett wrote:

Hi again, mh,

I see what you're thinking now. Creating a fingerprint isn't very useful if you're just going to use it to make sure some HTTP headers remain consistent. For that, you can simply compare those values, and there's no reason to use md5() or salting at all.

This technique, which is valid but a little outdated, is to create a fingerprint that is stored on both the client and the server. It is passed using a different method of propagation than the cookie, so that even if the cookie and all HTTP headers are captured by an attacker, they cannot be replayed to hijack the session. Something extra is needed.

The real purpose of the article is to help you understand how sessions work, what some of the potential weaknesses are, and how you can enhance it slightly to complicate some of the more common attacks.

I'm more fond of trending these days, where you record trends in a particular user's requests and use that to detect anomalies in behavior. If you get the balance right, session hijacking becomes very difficult, and legitimate users aren't constantly being prompted for their password.

Hope this helps.

Fri, 15 Jan 2010 at 02:29:23 GMT Link


76.Giovanni wrote:

Hi Chris!

First of all, my persona thanks for all your article about PHP security! it's really usefull and easy to read ;)

i have a question about a portion of che code that you wrote

if (md5($_SERVER['HTTP_USER_AGENT']) != $_SESSION['HTTP_USER_AGENT'])

so, can i be sure that HTTP_USER_AGENT is always send by the browser? why not use the IP of the user, with $_SERVER['REMOTE_ADDR']?

and when, could be more sure if i save it in a db and then check se session value with the one in the db?

Thanks for all!!!

Giovanni

Wed, 20 Jan 2010 at 09:33:31 GMT Link


77.Maksym Kozub wrote:

Thank you for an interesting article. The PHP Magazine URL ha changed; now it can be found at http://phpmagazine.net, not http://phpmag.net.

By the way, I have been unable to register at your site with any of my OpenIDs, including the ones from LiveJournal, Google, and ClaimID.

Tue, 16 Feb 2010 at 10:55:34 GMT Link


78.Rob wrote:

Absolutely great article. Really well written and easyn to understand.

Wed, 07 Apr 2010 at 13:48:54 GMT Link


79.Roy wrote:

Excellent article!

Just curious, would there be any benefit or downside (in addition to the article) to change the SID cookie name to something generic, say: session_name("foo"); and then set a cookie that is part of your fingerprint as the default SID cookie name: setcookie("PHPSESSID", .., ..); just to cause more confusion for an attacker?

Tue, 11 May 2010 at 20:57:49 GMT Link


80. wrote:

Just a perfect article. Thanks a lot for your writing. I just read it to refresh my knowledge around the SESSIONS.

And again !

JUST PERFECT ARTICLE ! :)

Tue, 06 Sep 2011 at 17:46:04 GMT Link


81.Etaigbenu Canaan wrote:

This is a really good article. Thanks

Mon, 02 May 2016 at 13:15:42 GMT Link


Hello! What’s your name?

Want to comment? Please connect with Twitter to join the discussion.