|
|
|
|
|
|
| |
Sessions are an important part of web applications. They help applications balance the line between useful and useless. This line can be a moving target and sessions help us hit it in more than one way. Thanks to sessions, I don't have to supply my username and password everytime I request a different message from a web based e-mail application. Similarly, because of sessions, I can be sure that I will indeed receive the e-mail messge that I request. An important question that I think all web application developers should be asking is, how sure can I be that nobody else is reading my e-mail?
State and Session
The concepts of session and state are closely related. State is established when a client makes a request to a server for the first time and the server tells the client how it should identify itself from that point on. On subsequent requests if the client identifies itself in that way, the server assumes that it is the same client that it originally made the arrangements with. The most common client used to access web applications is a web browser. A session takes advantage of this uinque identification to store information related to a client.
In PHP, state and session are conveniently rolled into the built in session management functions. Programatically they are both established or confirmed by making a call to session_start(). State is established by way of a unique identifier. By default, the unique identifier that the server gives the browser is named PHPSESSID. This variable is stored on the client in either a URL, or a cookie. PHP first tries to set a cookie. If a browser does not accept cookies, PHP will append PHPSESSID to all URLs. |
|
<?php
session_start();
if (isset($_COOKIE['PHPSESSID'])) {
printf('PHPSESSID = %s', $_COOKIE['PHPSESSID']);
} else if (isset($_GET['PHPSESSID'])) {
printf('PHPSESSID = %s', $_GET['PHPSESSID']);
} else {
print('new session');
}
?>
|
|
|
This code is a simple example of how state is established. It checks to see if PHPSESSID is sent in a cookie or the URL. If it is not, we assume this is the first time the server has seen this client. If either of them are, we assume that this client has previously made a request. |
|
<?php
session_start();
if (isset($_SESSION['time_created'])) {
$time_since_start = time() - $_SESSION['time_created']
printf('<p>Hello %s, ', session_id());
printf('your first visit was %d seconds ago.', $time_since_start);
} else {
$_SESSION['time_created'] = time();
printf('new session created at %s', $_SESSION['time_created']);
}
?>
|
|
|
|
Once state is established we use sessions to store information about the client. The above code is an example of using the superglobal $_SESSION to do this. It looks for the variable $_SESSION['time_created']. If it exists it shows the difference between it and the current time. Otherwise it is created.
Security
Now that we have a few ways of establishing a session, we can take a look at each with security in mind.
Sometimes it is by accident that security is compromised. Take for example an application that manages sessions by passing the session identifier in the URL. I am logged in to this site and therefor have established a session. I find an interesting link and decide to e-mail it to a friend. They copy the URL into their browser and now have access to my session because their client told the server that they are me. If the session only gives the client access to insignificant information about me, then no real harm is done. On the other hand, if, from the URL I e-mailed, my friend can access more personal information, using this method must be reconsidered.
It is also possible that an attacker could gain access to server logs thereby having a whole slew of URLs with PHPSESSIDs in them to play with. It is not uncommon for system administrators to share log files for analysis. When Code Red hit I remember at least one person or organization that was requesting logs. As developers it might be worth consider this information public we design applications.
Using cookies to hold the session identifier will keep me from e-mailing my session to a friend. You can force PHP to only use cookies by enabling 'session.use_only_cookies' in your php.ini file. This method feels more secure because the session identifier is seemingly less visible, however, this is not necessarily the case.
There are numerous exploits that disclose cookie information. For example, in 'Passport Hacking' [0] and 'Passport Hacking Revisited' [1], Chris Shiflett illustrates the insecurity of an application that relies solely on cookies for authentication. This goes beyond what I am trying to cover here. For more information read the above artilce or search on goolge or the BugTraq archives.
What Is Enough?
The URL and cookie methods for managing sessions might be perfectly fine for some applications. If your data is more sensitive, it might be worth adding additional layers of security. The following is a quote from the online PHP manual [2] and I completely agree.
"The session module cannot guarantee that the information you store in a session is only viewed by the user who created the session. You need to take additional measures to actively protect the integrity of the session, depending on the value associated with it". -- php.net
Additional Measures
One possible method for increased security is to check a combination of information that the client sends on each request. Assuming that we want to use sessions, the built in session management will already be looking for PHPSESSID in either a cookie or URL variable. The User-Agent sent in the HTTP header [3] is a good candidate. This is commonly used to identify which browser is being used and to serve content that is compatible with that browser. To give you an idea of what the User-Agent looks like, below is what your client sent to the server.
|
|
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705)
|
|
|
Now we can store the User-Agent in a session variable and compare what the client sends the server to what we got on the original request. This is a good start, however, with MS Internet Explorer having almost 90% of the browser market [4], an attacker might guess that and send the correct User-Agent header. This of course presumes that the attacker knows (or guesses) that we are checking the User-Agent. We can increase the difficulty of guessing by checking other HTTP header values. Two other common headers are Accept and Accept-Encoding. Again, below is what your client sent to the server. |
|
Accept: */*
Accept-Encoding: gzip, deflate
|
|
|
To simplify the checing process, these along with User-Agent can be used to build a "fingerprint" of the client on the original request. For the client to properly identify itself on every request after that, all three values must be exactly the same as they were in the initial request. The difficulty of guessing can be increased even more by constructing the fingerprint from more headers. Some browsers may not send all the headers you are using. If you try to reference a header that isn't defined, PHP will raise an E_NOTICE error. Be sure you are prepared to handle this situation. |
|
<?php
session_start();
$client_fingerprint = md5($_SERVER['HTTP_USER_AGENT'].$_SERVER['HTTP_ACCEPT'].$_SERVER['HTTP_ACCEPT_ENCODING']);
if (isset($_SESSION['fingerprint'])) {
if ($client_fingerprint == $_SESSION['fingerprint']) {
$elapsed_time = time() - $_SESSION['last_hit_time'];
printf('Welcome back: %s<br/>', $_COOKIE['PHPSESSID']);
printf('Elapsed lapse: %d seconds', $elapsed_time);
$_SESSION['last_hit_time'] = time();
} else {
print('<b>ACK!</b><br/>');
printf('client: %s<br/>', $client_fingerprint);
printf('server: %s<br/>', $_SESSION['fingerprint']);
printf('cookie: %s<br/>', $_SERVER['HTTP_COOKIE']);
}
} else {
$_SESSION['fingerprint'] = $client_fingerprint;
$_SESSION['last_hit_time'] = time();
print('new session');
print('<a href="three.php">here</a>');
}
?>
|
|
|
The fingerprint is created by generating an md5 hash of three of the HTTP header values. It is used to check for the existence of a session and then the fingerprint stored in the session is checked against the fingerprint of the current request to authenticate the client. The example only prints some information, but could easily set a flag or raise an exception.
Conclusion
This method does not guarantee the security of an application. However, it does increase the security of an application by increasing the amount of effort required to compromise a session. Hopefully it has helped to clarify some of the issues around application security and presented one way of addressing security programmatically.
References
[0] - http://shiflett.org/articles/passport_hacking/
[1] - http://shiflett.org/articles/passport_hacking_revisited/
[2] - http://www.php.net/manual/en/ref.session.php
[3] - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
[4] - http://www.upsdell.com/BrowserNews/stat.htm
|
|
| |
| PHP5: Designing And Using Interfaces Categories : PHP, Object Oriented, Interfaces, PHP Classes, Security | | | PHP 101 Part 10 of 15 : A Session In The Cookie Jar Categories : PHP, Beginner Guides, Cookies, Sessions | | | PHP for Beginners by a Beginner: Simple Login, Logout, and Session Handling Categories : PHP, Sessions, Authentication | | | Protecting PHP Scripts with HTTP Authorization Categories : PHP, HTTP, Security, Authentication | | | Building a PHP Style Switcher Categories : PHP, Sessions, Personalization and Membership | | | Writing A Port Scanner In PHP Categories : PHP, HTTP, Security | | | Working with Permissions in PHP, Part 1 Categories : PHP, Security | | | Developing Custom PHP Sessions Categories : PHP, Sessions | | | User Authentication With patUser (part 2) Categories : PHP, Authentication, Security | | | 10 PHP Functions I Bet You Didn't Know About! Categories : PHP, PHP Functions, Filesystem, Arrays, Errors and Logging | | | Developing a Security Policy, by Anna Johnson Categories : Other, Security, Site Planning | | | Generating One-Time URLs with PHP Categories : PHP, URLs | | | Data, its presentation and user interface forms Categories : PHP, XML, User Interface | | | Using the .NET Assembly in PHP Categories : PHP, .NET | | | PHP 101 Part 8 of 15 : Databases and Other Animals Categories : PHP, Beginner Guides, Databases | |
| |
|
|