I have finally gotten around to starting a blog and this will be my first post in what is hopefully a long running series of useful secure development posts. Given that I am in the business of application security, specifically web applications, I have decided to do a series on OWASP protections. I will try and do a series for several major languages (perl, PHP, Java, ASP.NET), starting off with PHP. I am going to randomly select OWASP Top 10 of 2010 risks and provide countermeasures in the current language for the series. I am going to try to cover all of the verification requirements listed in the OWASP Application Security Verification Standard (ASVS) Project.
I know that this has probably been done before somewhere. I just wanted to create a central repository for myself and others on developing secure applications. I will probably learn just as much as anyone else reading this blog along the way.
I’m not perfect and will certainly make mistakes; either with inefficient code or just plain bad code. Please let me know if you see something that is wrong, off, or could be done a little better.
So without further ado…
As the title suggests, this post will cover part of OWASP A3 – Broken Authentication and Session Management (2010). There are 13 ASVS validation requirements, so I am going to break this up into several posts.
ASVS 3.1 Requirement:
Verify that the framework’s default session management control implementation is used by the application.
ASVS 3.1 Solution:
This is a fairly obvious one. If you are using PHP, then utilize the native PHP session management functionality in your application. Do not roll your own session management unless it is absolutely necessary (which is usually not the case). The default or native session management functionality in common systems is usually more secure.
ASVS 3.2 Requirement:
Verify that sessions are invalidated when the user logs out.
ASVS 3.2 Solution:
Ensure that all session data is deleted if a user logs out, such that no one can use the associated session ID’s or cookies to gain access to the application. If the session isn’t properly invalidated, then a malicious individual might be able to obtain authenticated access to your application under another user’s account via a session ID.
The steps in PHP are:
- Resume the current session so that it can be destroyed.
- Destroy the current session.
- Unset the session ID (destroying the session does not unset global variables).
- Delete associated cookies (destroying the session does not delete cookie data).
- Redirect to the login page.
The following code can be placed in a logout.php file that is called when the user logs out:
<?php // Resume the session so that it can be deleted session_start(); // Destroy the current session session_destroy(); // I like to make sure the session cannot be cached by either the client or a proxy. // This can be accomplished with the following: session_cache_limiter('nocache'); // Unset all session variables, including the session ID $_SESSION = array(); // Kill the session by deleting the session cookie. This will destroy the session // and session data if (ini_get("session.use_cookies")) { $params = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"] ); } // Redirect to the login page: login.php header('Location: login.php'); // Stop all further processing on the page die(); ?>
ASVS 3.3 Requirement:
Verify that sessions timeout after a specified period of inactivity.
ASVS 3.3 Solution:
Ensure that sessions timeout if a user steps away from the application for an extended period of time.
The steps in PHP are:
- Resume the current session so that it can be destroyed.
- Check to see if the session variable used to track activity has been set. If not, destroy any session data and redirect to the login page.
- Check to see if the current time minus the time recorded in the last activity is greater than 900 seconds (15 minutes). If the inactivity timer has been exceeded, then destroy any session data and redirect to the login page.
- If the activity tracking session variable is set and the 15 minute timer has not elapsed then update the variable to the most recent activity time.
The following code can be placed in a header file that is included by every page:
<?php session_start(); session_cache_limiter('nocache'); // Check to see if the session variable used to track user activity has been set, // if not then destroy all session data and redirect to the login page. If it has // been set, compare current time minus the last activity time, and if it is // greater than 900 seconds (15 minutes), then destroy session data and // redirect to the login page. if (!isset($_SESSION['Last_Activity']) || ((time() - $_SESSION['Last_Activity']) > 900)) { session_destroy(); $_SESSION = array(); if (ini_get("session.use_cookies")) { $params = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"] ); } header('Location: login.php'); die(); } else { // If the session variable for tracking activity has been set AND we have seen // activity within the last 15 minutes, then update the last activity timer with // the current time. $_SESSION['Last_Activity'] = time(); } ?>
Bonus:
I like to create a checkSession.php file with the above code, with the following exceptions:
- Remove the else statement.
- Replace the header function with the following:
print("login.php");
I then create a small bit of AJAX to call this page in the background every minute. If the session goes stale then it will automatically logout, destroy session data, and redirect the user to the login page. Otherwise, nothing will happen until a user does something in the application (I hope that make sense).
The AJAX code:
<script> // Create a javascript variable to track the 60 second interval checks var interval // Create the AJAX function that will be called every 60 seconds to validate the user's session function checkSession() { var http; // Standard AJAX setup code, beyond the scope of this post if (window.XMLHttpRequest) { http = new XMLHttpRequest(); } else if (window.ActiveXObject) { http = new ActiveXObject(\"Msxml.XMLHTTP\"); if (! http) { http = new ActiveXObject(\"Microsoft.XMLHTTP\"); } } // When the function is called, check the return data of the checkSession.php code, if the data // contains login.php (from the print statement), then redirect to the login.php page http.onreadystatechange = function() { if (http.readyState == 4) { if (http.responseText == 'login.php') { window.location.href=http.responseText; } } } // When the function is called, make a request to checkSession.php http.open(\"GET\", \"checkSession.php\", true); http.send(null); } // On loading this page, set the interval variable. Run the function every 60 seconds. window.onload = function() { interval = setInterval('checkSession()', 60*1000);// 60 secs between requests }; </script>
I think that about does it for this week. Next week I will pick up where we left off. Questions, comments, and feedback are always welcome. Until next week…
Leave a Reply