Codepath

Session Hijacking

Session hijacking is an attack where the attacker steals a user's active session with a website to gain unauthorized access to actions and information on that website.

Sessions store information about a user on the server-side, usually either in a file or a database. Sessions are more secure than putting user data into browser cookies because the data being stored never leaves the server. However, to identify the user and give them access to the session data, it is necessary to set a session reference identifier ("session ID") in a browser cookie. This session ID is vulnerable to theft because cookies are visible in storage and in transit. (Every request to the server will send visible cookie data.) Discovering the session ID provides an attacker access to all session data. But even worse, the attacker can impersonate the user. This is called "session hijacking".

The server must assume that any request including a user's session ID must be originating from the user's browser. An attacker can send a request with the user's session ID and assume for themselves any previous state set in the session. The session is often used to maintain the user's logged-in state or other authorization to perform access-restricted actions. A hijacker with a logged-in session can perform any action which the user could perform. They can transfer money. They can view and edit personal information. They can change the account password, which will lock out the real user. They can impersonate the user and send communications to friends and coworkers as a spear phishing attack (see Social Engineering).

Session hijacking requires an attacker to determine the session ID. The session ID is vulnerable in storage and in transit. In storage, the session ID can be stolen from the user's browser cookies, often via Cross-Site Scripting (XSS). In transit, the session ID can be observed by eavesdropping on the network traffic. Remember, the session ID is sent with every request to the server.

It is especially easy for an attacker to eavesdrop by inspecting all traffic on an open and unencrypted wireless network, such as the free WiFi offered at coffee shops and other businesses. When communicating over WiFi, a laptop or mobile device broadcasts a request "into the room" and the WiFi device in the room receives the signal. But these broadcasts are also visible to any other device in the room, including an eavesdropping attacker.


Session Hijacking Preventions

The first prevention is to use HttpOnly cookies for setting session IDs. This technique is one of the standard preventions for Cross-Site Scripting (XSS). HttpOnly cookies prevent an attacker from discovering the stored session ID using at XSS attack.

Another good prevention is to fully destroy sessions whenever a user logs out. Often, the user's authentication status is removed but the session itself is retained for re-use. A hijacker with possession of a logged-out session can simply wait for the session to be logged-in again.

<?php
  session_start();

  // use both unset and destroy for compatibility
  // with all browsers and all versions of PHP
  session_unset();
  session_destroy();
?>

It is also a best practice to expire and remove old session files regularly. Fewer sessions in existence means fewer sessions which can be hijacked.

The most common technique is to track either the user's last activity or their last login (or both). Last activity can be tracked by inspecting the "last modified" timestamp of a session file, or by automatically updating a "last modified" field for sessions stored in a database table. A script is then written which will run at timed intervals (every midnight, every four hours, etc.) and which will remove stale sessions. This process is called "session sweeping".

In addition to or instead of sweeping, code can check for old sessions whenever a new request is received. This is an example in PHP showing how to determine if a user's last login was more than one day ago. If the test fails, then the session could be regenerated (see below) or the user could be required to log in again.

<?php
  // After a successful login
  $_SESSION['last_login'] = time();

  function last_login_is_recent() {
    $recent_limit = 60 * 60 * 24 * 1; // 1 day
    if(!isset($_SESSION['last_login'])) { return false; }
    return (($_SESSION['last_login'] + $recent_limit) >= time());
  }
?>

A strong defense against session hijacking is to regenerate session identifiers periodically and at key points. Regenerating a session ID invalidates any previously stolen session IDs. It is most important to regenerate the session ID after a successful login. Any existing session information is maintained, it is only the identifier which gets refreshed.

<?php
  function after_successful_login() {
    session_regenerate_id();
    $_SESSION['logged_in'] = true;
    $_SESSION['last_login'] = time();
  }
?>

Regenerating a session ID will not protect against a recently stolen session ID, but it will keep session IDs fresh and force an attacker to use a recent session ID. Regenerating session IDs after a login is also a major defense against Session Fixation Attacks which makes it a worthwhile defense to have.


The best defense against session hijacking is to force secure, encrypted communications over TLS/SSL. This is also sometimes called "HTTPS". Cookies will still be sent with every request but their contents will not be visible because the entire communication will be encrypted while in transit.

When implementing SSL, there are three key measures that should be taken:

  1. Users must log in over SSL. The login form should be a secure page and it should submit the login form to a secure page. The user's credentials will be encrypted and the session ID which is sent back will be encrypted. Eavesdropping will not be possible.

  2. Additional requests and responses for access-restricted pages must be over SSL. Remember, the session ID is being sent with every request. Therefore every request must be encrypted if the session ID is to remain a secret.

  3. You must use a "secure cookie" to store the session ID. Cookies can configured to be "secure" which ensures that the cookie will only be sent over a secure connection. Without this setting turned on, any visit to a non-SSL page on the same domain could send and expose the cookie containing the session ID.

Many web services include SSL-only as a user preference, and many others have begun making it automatic and mandatory. For the last several years, Google Search includes HTTPS in their ranking algorithms.

The Electronic Frontier Foundatino (EFF) is running a well-publicized campaign for "HTTPS Everywhere". Their goal is to make SSL easier to implement and encourage developers to make it a default.


One common bit of advice to prevent session hijacking is to confirm that the user-agent string (the browser type) for the request matches the user-agent string used at login. The idea is that the user is probably not changing browsers between requests.

At login, the application stores the user-agent string in the session file. With each request the application inspects the new user-agent string and compares it with the stored one. If the test fails, then the session could be regenerated or the user could be required to log in again.

This technique only offers a small defense. The user-agent string is visible in the request headers. An attacker eavesdropping on network traffic would see the correct string and could easily fake a request with the correct string.

It has also become more common for users to move between devices (desktop, laptop, tablet, phone). A user-agent string could be stored per-device, but if it is stored per-user, then a choice must be made whether it is desirable to force a user to re-login whenever they change devices.

<?php
  // After a successful login
  $_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];

  function user_agent_matches_session() {
    if(!isset($_SESSION['user_agent'])) { return false; }
    if(!isset($_SERVER['HTTP_USER_AGENT'])) { return false; }
    return ($_SESSION['user_agent'] === $_SERVER['HTTP_USER_AGENT']);
  }
?>

Similar to checking the user-agent string, sometimes a check of the IP address will be recommended as a session hijacking defense. This check is not recommended because it is very unreliable and buggy. A user's IP address may change. This is especially true with the increased use of mobile devices which move seamlessly between wireless access points and cell phone towers. It is also true that many computers can share the same IP address. This is very common in corporate environments and on wireless networks. If an attacker is in the same coffee shop observing a user's wireless traffic, they could already be using the same IP address.


Fork me on GitHub