SessionBox – Log in with multiple accounts to any website

For some online services such as Twitter and Facebook I have more than one account. Be it accounts for my company, an alter ego, my children, etc.

To easily switch between these accounts – without logging out, back in, and doing the 2FA dance – I rely on a Chrome Extension named SessionBox which allows me to do just that.

Using websites with multiple accounts at the same time is made easy. Create an independent tab with a click of a button.

Use one browser with multiple active sessions per site. Log into multiple accounts on the same site simultaneously. No more need for secondary browsers, private sessions, let SessionBox handle these for you.

SessionBox on the Chrome Web Store →

πŸ”₯🦊 Firefox user?

The Firefox Multi-Account Containers Add-On does a similar job.

PHP Session Locking: How to Prevent Blocking Requests

Today I learned about β€œPHP Session Locking”:

PHP writes its session data to a file by default. When a request is made to a PHP script that starts the session (session_start()), this session file is locked. What this means is that if your web page makes numerous requests to PHP scripts, for instance, for loading content via Ajax, each request could be locking the session and preventing the other requests from completing.

The other requests will hang on session_start() until the session file is unlocked. This is especially bad if one of your Ajax requests is relatively long-running.

As a developer you can prematurely close the session by calling session_write_close() or – with PHP7 – close it automatically after starting:

session_start([
    'read_and_close' => true,
]);

PHP Session Locking: How To Prevent Sessions Blocking in PHP requests →
PHP Session Locks – How to Prevent Blocking Requests →

Securing Sessions in PHP

php-logo

I set out to combine all the best practice I could find into a single Session handler, to help protect against the common attack vectors. Since PHP 5.4, you are able to set the Session handler based on a class instance that extends the default SessionHandler class.

Make the session cookie only available over HTTP, encrypt data when storing it in the session, rotate the session id from time to time (one should also do this after key actions such as logging in / out by the way), don’t make everlasting sessions, and limit sessions linked to the IP that started it.


<?php

class SecureSessionHandler extends SessionHandler {

    protected $key, $name, $cookie;

    public function __construct($key, $name = 'MY_SESSION', $cookie = [])
    {
        $this->key = $key;
        $this->name = $name;
        $this->cookie = $cookie;

        $this->cookie += [
            'lifetime' => 0,
            'path'     => ini_get('session.cookie_path'),
            'domain'   => ini_get('session.cookie_domain'),
            'secure'   => isset($_SERVER['HTTPS']),
            'httponly' => true
        ];

        $this->setup();
    }

    private function setup()
    {
        ini_set('session.use_cookies', 1);
        ini_set('session.use_only_cookies', 1);

        session_name($this->name);

        session_set_cookie_params(
            $this->cookie['lifetime'],
            $this->cookie['path'],
            $this->cookie['domain'],
            $this->cookie['secure'],
            $this->cookie['httponly']
        );
    }

    public function start()
    {
        if (session_id() === '') {
            if (session_start()) {
                return mt_rand(0, 4) === 0 ? $this->refresh() : true; // 1/5
            }
        }

        return false;
    }

    public function forget()
    {
        if (session_id() === '') {
            return false;
        }

        $_SESSION = [];

        setcookie(
            $this->name,
            '',
            time() - 42000,
            $this->cookie['path'],
            $this->cookie['domain'],
            $this->cookie['secure'],
            $this->cookie['httponly']
        );

        return session_destroy();
    }

    public function refresh()
    {
        return session_regenerate_id(true);
    }

    public function read($id)
    {
        return mcrypt_decrypt(MCRYPT_3DES, $this->key, parent::read($id), MCRYPT_MODE_ECB);
    }

    public function write($id, $data)
    {
        return parent::write($id, mcrypt_encrypt(MCRYPT_3DES, $this->key, $data, MCRYPT_MODE_ECB));
    }

    public function isExpired($ttl = 30)
    {
        $last = isset($_SESSION['_last_activity'])
            ? $_SESSION['_last_activity']
            : false;

        if ($last !== false && time() - $last > $ttl * 60) {
            return true;
        }

        $_SESSION['_last_activity'] = time();

        return false;
    }

    public function isFingerprint()
    {
        $hash = md5(
            $_SERVER['HTTP_USER_AGENT'] .
            (ip2long($_SERVER['REMOTE_ADDR']) & ip2long('255.255.0.0'))
        );

        if (isset($_SESSION['_fingerprint'])) {
            return $_SESSION['_fingerprint'] === $hash;
        }

        $_SESSION['_fingerprint'] = $hash;

        return true;
    }

    public function isValid()
    {
        return ! $this->isExpired() && $this->isFingerprint();
    }

    public function get($name)
    {
        $parsed = explode('.', $name);

        $result = $_SESSION;

        while ($parsed) {
            $next = array_shift($parsed);

            if (isset($result[$next])) {
                $result = $result[$next];
            } else {
                return null;
            }
        }

        return $result;
    }

    public function put($name, $value)
    {
        $parsed = explode('.', $name);

        $session =& $_SESSION;

        while (count($parsed) > 1) {
            $next = array_shift($parsed);

            if ( ! isset($session[$next]) || ! is_array($session[$next])) {
                $session[$next] = [];
            }

            $session =& $session[$next];
        }

        $session[array_shift($parsed)] = $value;
    }

}

$session = new SecureSessionHandler('cheese');

ini_set('session.save_handler', 'files');
session_set_save_handler($session, true);
session_save_path(__DIR__ . '/sessions');

$session->start();

if ( ! $session->isValid(5)) {
    $session->destroy();
}

$session->put('hello.world', 'bonjour');

echo $session->get('hello.world'); // bonjour

Note that the example on line 172 should read $session->forget(); instead of $session->destroy();

Securing Sessions in PHP →
PHP: The SessionHandler class →