7. Securing the application
7.3. The application security
7.3.1. Secure session management
In the secure version of the ButterFly, the changes in the PHP session configuration concern two changes:
• The path for storing session information was changes to the custom folder where only the web server has access to (/apache/www/apache22/butterfly/sessions)
• The use_only_cookie option was enabled, which means that the session tokens will be accepted only from the cookies.
In the next sections I will present specific solutions to the session vulnerabilities found in the ButterFly application.
7.3.1.1. Session fixation
Frankly speaking, the use_only_cookie option enabled ‘solves’ the problem of the session fixation vulnerability. It does that, because the attacks with session token (sid) in GET or POST requests are ignored by the application in this case. But as you can see, this option does not really solve the problem, it only limits the attack vectors.
In order to correct this vulnerability 100%, I have to introduce a new security measure. But before that, I will explain shortly how this vulnerability works.
In PHP built-in session management, a session token is usually generated by the session mechanism. However, in case when a session token is provided already in the request, the session mechanism accepts this value by creating the session file in defined path by session.save_path parameter. After a user authenticates successfully, the PHP session mechanism puts authenticated session tokens into the session file. An attacker can have full access to the application, because he knows the session token. We cannot change the fact that the PHP session mechanism accepts session tokens set by a user/attacker without writing a custom PHP session management system. However, it is possible to solve this vulnerability easily by using session_regenerate_id function.
Even when we can not protect ourselves from accepting the session token from a user, if we regenerate the session token to the value generated by the server, after a user authenticates successfully. The session fixation attack will be worthless, because only the session token generated by the server will be authenticated! Further more, if we use session_regenerate_id(‘t’), the previous session token will be removed from the server completely. Therefore, even if an attacker manages to convince a user to click a link with the malicious SID value, after a user authenticates, the session token set by an attacker will be worthless, because the server will create new session token and remove the old one. Let’s check how effective this solution is. But first we have to comment out the php_value session.use_only_cookies option in virtual host configuration. With this option our test will not work properly. After restarting the ButterFly environment, let’s enter the following link:
http://secure.butterfly.prv/index?&sid=111222333
You should be redirected to the login page shortly after entering the above link. However, look what was created in session SAVE_PATH folder:
test butterfly # ls -la apache/www/apache22/butterfly/sessions/ total 8
Securing PHP applications
-rw--- 1 bfly1 bfly1 0 Mar 5 11:43 sess_111222333
The session token supplied by me was accepted by the application. So this first part of the attack is still successful. Let’s check what happens when a user logs to the application next. The following request and answer from the server are sent:
POST http://secure.butterfly.prv/login?req=/index?&sid=111222333 HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/msword, application/x-silverlight, */*
Referer: http://secure.butterfly.prv/login?req=/index?&sid=111222333 Accept-Language: en-gb
Content-Type: application/x-www-form-urlencoded UA-CPU: x86
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727) Paros/3.2.13 Proxy-Connection: Keep-Alive Content-Length: 40 Host: secure.butterfly.prv Pragma: no-cache username=app1&password=app1&auth_b1=Send HTTP/1.1 302 Found
Date: Wed, 05 Mar 2008 11:49:35 GMT Server: Apache
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache
Set-Cookie: sid=lga2pa8crla3kbnbi48obo09v8fj57fk; path=/; domain=secure.butterfly.prv Location: http://secure.butterfly.prv/index?
Content-Length: 0 Content-Type: text/html
You will notice that the new session cookie was set by the ButterFly application. But what about the session token set by the attacker? Let’s check the content of the folder where session tokens are kept:
test butterfly # ls -la apache/www/apache22/butterfly/sessions/ total 12
drwxr-x--- 2 bfly1 bfly1 4096 Mar 5 11:49 . drwxr-xr-x 6 root root 4096 Feb 21 12:46 ..
-rw--- 1 bfly1 bfly1 44 Mar 5 11:49 sess_lga2pa8crla3kbnbi48obo09v8fj57fk
There is no sign of the old session token. Only the new regenerated value is stored on the server side. The session fixation was stopped effectively.
7.3.1.2. CRLF injection/HTTP Response Splitting
As you probably remember, the HTTP Response Splitting vulnerability was corrected by the PHP Team in the PHP version 5.1.2.
When you try to attack the PHP in this version, the PHP will throw the following error:
[Tue Feb 12 14:09:21 2008] [error] [client xx.xx.xx.xx] PHP Warning: Header may not contain more than a single header, new line detected. in /apache/www/apache22/butterfly/secure/login.phpsec on line 63, referer:
http://secure.butterfly.prv/login?req=/orders?aaa%0a%0dbbbb
However, always it is better to eliminate the source of the problem, handle the problem correctly and do not count on the ‘external’ solutions.
In case of the HTTP Response Splitting, we have really two options for possible solutions.
The first option is to use the variables that do not interpret the 0x0a, 0x0d special character. The following variables belong to this group: _SERVER["QUERY_STRING"], _SERVER ["REQUEST_URI"]. The redirect_to_login function uses one of these variables and because of that is not vulnerable to this attack (as you could see in the 6.1.2 section). However, authenticating function in login.php uses $_GET variable, which with $_POST and $_REQUEST variables, interpret these special characters and is vulnerable to the HTTP Response Splitting attack.
The second option, in my opinion the best option, is to encode properly data received from a client. Using the following code:
$redir .= filter_input(INPUT_GET, 'req', FILTER_SANITIZE_SPECIAL_CHARS);
instead of
$redir .= $_GET['req'];
will solve the problem completely. The special character will be encoded in the HTTP header as you can see below:
POST http://secure.butterfly.prv/login?req=/orders?aaa%0a%0dbbbb HTTP/1.1 Content-Type: application/x-www-form-urlencoded
Content-Length: 40 Host: secure.butterfly.prv HTTP/1.1 302 Found
Date: Tue, 12 Feb 2008 15:07:05 GMT Server: Apache
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0 Pragma: no-cache
Set-Cookie: sid=ravft125dclc62ge72shunmil8vnvo8j; path=/; domain=secure.butterfly.prv Location: http://secure.butterfly.prv/orders?aaa bbbb
Content-Length: 0 Content-Type: text/html
7.3.1.3. Session Replay
In order to understand the Session Replay vulnerability, we need to take a look at the logout.php file:
<?
include("functions.php"); db_connect();
# clear the cookie
setcookie (session_name(), "", time()-42000, "/", $_SERVER['HTTP_HOST']); redirect_to_login("/index.php");
exit(); ?>
As you can see the logout procedure only removes the session data on a client side. From a web browser perspective this solution works, the session cookie is removed and it is not possible to access the application anymore. However, using additional tools it is very easy to replay previous requests and get access to the application.
In order to correct this vulnerability, we have to remove the session data on the server side as well. Adding the following code should solve the vulnerability. After a user clicks logout button, the session
Securing PHP applications
cookie will be removed from his browser and the session data stored on the server side will be removed as well.
#resume the session session_start(); # session id set?
if (isset($_SESSION['user'])) {
# Unset all of the session variables. $_SESSION = array();
# clear the cookie
setcookie (session_name(), "", time()-42000, "/", $_SERVER['HTTP_HOST']); # destroy the session.
session_destroy(); }
However, one problem will still remain with the Session Replay. When a user forgets to logout properly and just closes the browser, the session token will still remains valid. In order to protect partially against this problem the session timeout value is used. In case of the ButterFly application it is 15 minutes. However, this does not mean that the session will stop being valid exactly after 15 minutes.
You should remember that the garbage collector process is responsible for removing old session files. There are two session parameters (session.gc_probability and session.gc_divisor), which defines how often the garbage collector can be run. Therefore, it means that after 15 minutes the session is probably to be removed from the server. This depends on the two session parameter mentioned above and the amount of the requests to the applications.
If you do not like this behavior, you will have to write a custom PHP session management.
7.3.1.4. Unauthenticated access (search_ajax)
Authentication vulnerabilities are common in web applications nowadays, especially in AJAX pages. Sometimes, it is just a simple mistake, forgetting to put the authentication check into the code. This type of the vulnerability can be corrected very easily. This is the case of the ButterFly application. Putting the is_authenticated() function after db_connect() function solves the problem.
But this vulnerability can stem from completely different framework used to build AJAX pages. In this case, an AJAX page does not have to have a direct access to the session token stored by the main application. Sometimes, the AJAX pages can be handled by different servers, which complicates the situation even more.