## Cookie session driver

Cookie session driver saves entire session information into an encrypted and signed cookie.

## Installation

Installation is done via `composer`

```bash
$ composer require "buckhill/csession: *"
```


## Requirements

This package depends on `openssl` PHP extension.


## Required configuration

- Public and private key pair

- 32 character long secret (for encryption)

- 16 character long initialization vector (for encryption)

- Unique alphabet and salt for `Hashids` configuration

- Terminable middleware class which adds cookie information to `Response`


## Sample configuration file

```php
<?php

return [

    'cookie_name' => 'the_cookie_name_for_session',
    
    'domain' => 'authstack.co.uk',
    
    'expiry' => '+30 days',

    'encryption' => [
        'method' => 'aes-256-cbc', // use openssl_get_cipher_methods() to see what the possible values are
        'secret' => 'CcRQStOvc6dkQ98JuW0qnuv1GqQDCTuc',
        'iv' => 'vA7krGL6i9snmedy'
    ],

    //-----------------------------------------------------------------------------------------------------------------
    // Public Key Infrastructure - required for safe cookies
    //-----------------------------------------------------------------------------------------------------------------
    'pki' => [
        'private' => [
            'value' => file_get_contents(__DIR__ .'/pki/private.pem'), // Private key contents
            'passphrase' => '' // Passphrase, if there is any
        ],

        'public' => file_get_contents(__DIR__ . '/pki/public.crt'),

        'signature_algo' => OPENSSL_ALGO_SHA1 // check: http://php.net/manual/en/openssl.signature-algos.php
    ],

    'hashids' => [
        'alphabet' => '0p3rc9y8e76vaxqs',
        'min_length' => 16,
        'salt' => 'hKsUqLuBzF9z7rdI6MdLF2K0CFOsOmLz'
    ]
];

```

## Sample Service Provider for Laravel / Lumen

```
<?php

namespace App\Providers;

use CookieSession\Session;
use Illuminate\Support\ServiceProvider;


class CookieSessionProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton('CookieSession', function($app)
        {
			$config = config('cookiesession'); // Or the array mentioned previously        
        
            $session = Session::initialize($config);
            
            // You can unserialize the cookies from request here or you can do it in middleware            
            $session->unserializeFromRequest(app('request'));
            
            return $session;
        });
    }
}


```

## Sample global middleware

```php
<?php

namespace Illuminate\Session\Middleware;

use Closure;

class WriteSession
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);
        
        // Retrieve the session object using Service Provider    	
		$session = app('csession');
		
		// Fetch cookies from serializer
		$serialized = $session->toCookies();
		
		// Add each cookie to response
		foreach($serialized as $cookie)
		{
			$response->headers->setCookie($cookie);
		}
		
		return $response;
    }
}

```

## Encryption, secrets, practices used with this project

This project uses PHP's `openssl_encrypt` and `openssl_decrypt`. 

All encryption secrets are hashed using `hash('sha256')` before use. Every encryption iteration uses a different initialization vector, which is calculated like this:

```php
public function autoSetIV()
{
	$iv_length = openssl_cipher_iv_length($this->method);

	if(false === $iv_length)
	{
		throw new \Exception(sprintf("Unable to get cipher iv length for the specified cipher: %s", $this->method));
	}

	$iv = openssl_random_pseudo_bytes($iv_length);

	$this->iv = $iv;

	return $this;
}

```

Method is one of available encryption methods listed in `openssl_get_cipher_methods()` array.

The flow of secure storage is the following:

- session data is serialized into JSON format
- JSON data is encrypted
- The encrypted string is chunked so it can be saved across several cookies (depending on browser implementation, this goes up to 80 cookies, 4KB each per cookie)
- Metadata is calculated and it contains: signature of the encrypted data using public-private keypair and number of cookies that store the data (number of chunks).
- Metadata is encrypted, base64_encoded (so it can be transferred via HTTP) and stored into so-called metadata cookie
- Storage cookies are delivered in sequence. Cookie names correspond to their index, indexes being numbers from `0` - `n`. Those names are further encoded using `hashids` so they aren't immediately obvious.


The flow of decryption is the following:
- Metadata cookie is looked up, base64 decoded and decrypted.
- Number of cookies is looked up from metadata.
- `for ($i = 0; $i < $num_found_in_metadata...`) - each cookie is looked up, its contents taken and concatenated into a big string that represents encoded and encrypted JSON.
- PKI signature is calculated using the "big string" and compared with the info in Metadata cookie
- If the signature matches, the data is decoded and decrypted and finally - the string is `json_decode`d into proper PHP data structure and user session is once more - established