Home / Articles / Web Security
Web Security

Session Fixation: A Login Vulnerability That Often Goes Unchecked

Many websites already use strong passwords and HTTPS, but still forget to ensure that the session ID changes after login. Understand session fixation and practical steps to prevent it in PHP and WordPress applications.

Session Fixation: Celah Login yang Sering Lolos dari Pemeriksaan Website

Login security is not solely determined by the strength or weakness of the password. After a user successfully logs in, the website typically provides a session ID—a temporary identifier used by the server to recognize the user in subsequent requests. If this identifier can be forced or reused by others, the account can be taken over even if the password is never known.

This type of issue is known as session fixation. The name may sound technical, but the idea is simple: an attacker tries to make the victim use a session ID that the attacker already knows. When the victim logs in, that ID remains active and can then be used to access the victim's session.

How does session fixation occur?

Imagine a hotel giving a room number to a guest. Before the guest arrives, someone already knows that room number and manages to make the guest use that room. If the room number is not changed after the check-in process, that person could potentially misuse the information they already have.

On a website, that “room number” is the session ID. A simple scenario goes like this:

  1. The attacker obtains or creates a valid session ID.
  2. The attacker persuades the victim to open a URL, page, or specific flow that uses that session ID.
  3. The victim enters their username and password and successfully logs in.
  4. The server does not change the session ID after login.
  5. The attacker uses the same session ID to access the victim's account.

Such attacks are not always dramatically visible. There are no obvious fake login pages, no passwords to steal, and the victim may not see any symptoms. Therefore, checking the session lifecycle should be part of application security testing.

The main issue is not just cookies

Cookies are often where session IDs are stored, but the root of the problem lies in the server's behavior when creating and maintaining sessions. Enabling HTTPS alone is not enough if the application continues to use the same session ID before and after login.

Once the user's status changes from “not logged in” to “logged in,” the session identity should be updated. In PHP, the commonly used function is session_regenerate_id().

<?php
session_start();

if ($loginSuccessful) {
    session_regenerate_id(true);
    $_SESSION['user_id'] = $user['id'];
    $_SESSION['login_time'] = time();
}
?>

The true parameter requests PHP to delete the old session data. The actual implementation still needs to be adjusted according to the application's architecture, session storage mechanism, and the possibility of multiple concurrent requests. However, the principle is clear: regenerate the session ID after successful authentication, not just when the session is first created.

When to change the session ID is crucial

Login is not the only moment that needs attention. The session ID should also be considered for change when there are changes in user access rights or identity, such as:

  • The user successfully logs in.
  • The user switches from a regular account to administrator mode.
  • The user re-logs in for sensitive actions.
  • The account undergoes important role or permission changes.
  • The session is restored after additional authentication processes.

The goal is to prevent older sessions with lower trust levels from continuing to be used after the user's status increases. This serves as an additional layer of protection, not a replacement for multifactor authentication or proper access controls.

Session cookies must also be configured correctly

Session ID regeneration needs to be complemented by secure cookie settings. Three important attributes are Secure, HttpOnly, and SameSite.

  • Secure ensures that cookies are only sent over HTTPS connections.
  • HttpOnly prevents JavaScript from directly reading cookies. This helps reduce the impact of cookie theft via XSS, although it does not fix the root cause of XSS.
  • SameSite controls when cookies can be sent with cross-site requests, thus helping to reduce the risk of certain attacks like CSRF.

An example of basic configuration in PHP:

<?php
session_set_cookie_params([
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
]);

session_start();
?>

The SameSite value needs to be chosen based on the application's needs. Sites with login flows or cross-domain integrations may require different configurations. Importantly, these decisions should be made consciously, not left to default values without review.

Don't forget the logout process

A logout that merely removes the account view from the browser does not necessarily end the session on the server. The application should delete session data and make the session cookie invalid.

<?php
session_start();
$_SESSION = [];

if (ini_get('session.use_cookies')) {
    $params = session_get_cookie_params();
    setcookie(
        session_name(),
        '',
        time() - 42000,
        $params['path'],
        $params['domain'],
        $params['secure'],
        $params['httponly']
    );
}

session_destroy();
?>

For applications using centralized session storage or token systems, the invalidation process needs to follow the mechanisms used. The principle remains the same: old sessions should not continue to be considered valid after the user logs out.

What about WordPress?

WordPress administrators typically do not need to rewrite the core session system. However, risks can arise from plugins or themes that create their own login mechanisms, store tokens in cookies, or manage AJAX endpoints without adequate validation.

Some reasonable steps include:

  • Use WordPress, plugins, and themes from trusted sources and update them regularly.
  • Avoid plugins that create their own authentication systems without clear security documentation.
  • Check whether custom login processes replace tokens or sessions after authentication.
  • Ensure sensitive endpoints check user capabilities, not just the presence of cookies.
  • Use full HTTPS, including on login pages and administrative areas.

If a plugin has a custom login for customers, members, or vendors, that flow needs to be tested like a regular login application. Do not assume that the core security of WordPress automatically protects all additional logic created by plugins.

What you can do now

Start with simple testing in a staging environment. Note the session ID before login, perform the login, and then check if the ID changes. Repeat the testing when the user logs out, changes roles, and logs in again.

Next, check cookies through browser developer tools. Ensure that session cookies have the Secure, HttpOnly, and appropriate SameSite configurations. Also, review authentication logs for sessions used from unusual locations, devices, or time patterns.

Session fixation is not a vulnerability that is always easy to exploit, but its prevention is relatively inexpensive compared to the cost of recovering compromised accounts. By changing the session ID after login, securing cookies, and routinely testing authentication flows, websites have a much stronger foundation to protect user sessions.

Explore also

– Rio Yotto @rioyotto