Encrypt session data in PHP


As promised in my last post I present an example of strong cryptography in PHP to secure session data.
This is a very simple implementation that can be used to improve the security of PHP applications especially in shared environments where different users have access to the same resources. As you know, the PHP session data are managed by default using temporary files. In shared environment a malicious user that is able to access to these temporary files can easly read the session data because they are stored in plaintext (data in a session file is theserialization of the array $_SESSION).
In theory, the session data should be stored in folders that are accessible only by the owner of the web site, but never say never (btw, you can manage the location of the session data using the session_save_path function or changing the session.save_path in the php.ini).

To secure the session data I used strong cryptography to encrypt the content using the mcrypt extension of PHP. I chosen the simmetric cipher AES (Rijandel-256) to encrypt the session data and the openssl_random_pseudo_bytes() function to generate a random key of 256 bit.
The idea is to use a cookie variable to store the key that will be used to encrypt the session data. In this way the key is stored only in the client (the browser) and only the client is able to decrypt the session data on the server. Each time we encrypt the session data we re-generate the IV vector in a random way using the mcrypt_create_iv() function. It is very important to generate a unique IV in each encryption. This best practice increase the security of the encryption algorithm.
It’s important to note that this implementation is not secure against session hijacking attack. If someone is able to capture the cookie variable of a client and have access to the temporary session files, in the server, he/she will be able to decrypt the session data. Our goal is to protect session data against attacks on shared environments.

The idea to encrypt the session data is not new, for instance Chris Shiflett proposed an implementation in his book “Essential PHP Security” (O’Reilly, 2006). Shiflett used a $_SERVER variable to store the key used to encrypt the session data. Kevin Schroeder, my colleague at Zend Technologies, implemented a very similar session encryption algorithm extending the Zend_Session class of Zend Framework (you can find it here). In my solution, I used some of the best practices related to strong cryptography to implement a secure session handler.

Below the source code of my implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/**
 * ------------------------------------------------
 * Encrypt PHP session data using files
 * ------------------------------------------------
 * The encryption is built using mcrypt extension 
 * and the randomness is managed by openssl
 * 
 * @author Enrico Zimuel (enrico@zimuel.it)
 * @copyright GNU General Public License
 */
class SecureSession {
	const CIPHER= MCRYPT_RIJNDAEL_256;
	const CIPHER_MODE= MCRYPT_MODE_CBC;
	/**
	 * Key for encryption/decryption
	 * 
	 * @var string
	 */
	private static $_key;
	/**
	 * Path of the session file
	 *
	 * @var string
	 */
	private static $_path;
	/**
	 * Session name (optional)
	 * 
	 * @var string
	 */
	private static $_name;
	/**
	 * Size of the IV vector for encryption
	 * 
	 * @var integer
	 */
	private static $_ivSize;
	/**
	 * Cookie variable name of the key
	 * 
	 * @var string
	 */
	private static $_keyName;
	/**
	 * Generate a random key
	 * fallback to mt_rand if PHP < 5.3 or no openssl available
	 * 
	 * @param integer $length
	 * @return string
	 */
	private static function _randomKey($length=32) {
	     if(function_exists('openssl_random_pseudo_bytes')) {
    	          $rnd = openssl_random_pseudo_bytes($length, $strong);
        	  if($strong === TRUE) 
        	       return $rnd;
	     }
	     for ($i=0;$i<$length;$i++) {
	          $sha= sha1(mt_rand());
		  $char= mt_rand(0,30);
		  $rnd.= chr(hexdec($sha[$char].$sha[$char+1]));
	     }	
	     return $rnd;
	}
    /**
     * Open the session
     * 
     * @param string $save_path
     * @param string $session_name
     * @return bool
     */
    public static function open($save_path, $session_name) {
        self::$_path= $save_path.'/';	
	self::$_name= $session_name;
	self::$_keyName= "KEY_$session_name";
	self::$_ivSize= mcrypt_get_iv_size(self::CIPHER, self::CIPHER_MODE);
 
	if (empty($_COOKIE[self::$_keyName])) {
	     $keyLength= mcrypt_get_key_size(self::CIPHER, self::CIPHER_MODE);
	     self::$_key= self::_randomKey($keyLength);
	     $cookie_param = session_get_cookie_params();
             setcookie(
                  self::$_keyName,
                  base64_encode(self::$_key),
                  $cookie_param['lifetime'],
                  $cookie_param['path'],
                  $cookie_param['domain'],
                  $cookie_param['secure'],
                  $cookie_param['httponly']
             );
	} else {
	     self::$_key= base64_decode($_COOKIE[self::$_keyName]);
	} 
	return true;
    }
    /**
     * Close the session
     * 
     * @return bool
     */
    public static function close() {
        return true;
    }
    /**
     * Read and decrypt the session
     * 
     * @param integer $id
     * @return string 
     */
    public static function read($id) {
        $sess_file = self::$_path.self::$_name."_$id";
  	$data= @file_get_contents($sess_file);
  	if (empty($data)) {
  		return false;
  	}
  	$iv= substr($data,0,self::$_ivSize);
  	$encrypted= substr($data,self::$_ivSize);
  	$decrypt = mcrypt_decrypt(
             self::CIPHER,
             self::$_key,
             $encrypted,
             self::CIPHER_MODE,
             $iv
        );
        return rtrim($decrypt, ""); 
    }
    /**
     * Encrypt and write the session
     * 
     * @param integer $id
     * @param string $data
     * @return bool
     */
    public static function write($id, $data) {
        $sess_file = self::$_path.self::$_name."_$id";
	$iv= mcrypt_create_iv(self::$_ivSize, MCRYPT_RAND);
	if ($fp = @fopen($sess_file, "w")) {
	     $encrypted= mcrypt_encrypt(
                  self::CIPHER,
                  self::$_key,
                  $data,
                  self::CIPHER_MODE,
                  $iv
              );
	      $return = fwrite($fp, $iv.$encrypted);
	      fclose($fp);
	      return $return;
	 } else {
	      return false;
	 }
    }
    /**
     * Destroy the session
     * 
     * @param int $id
     * @return bool
     */
    public static function destroy($id) {
        $sess_file = self::$_path.self::$_name."_$id";
        setcookie (self::$_keyName, '', time() - 3600);
	return(@unlink($sess_file));
    }
    /**
     * Garbage Collector
     * 
     * @param int $max 
     * @return bool
     */
    public static function gc($max) {
    	foreach (glob(self::$_path.self::$_name.'_*') as $filename) {
    	     if (filemtime($filename) + $max < time()) {
      	          @unlink($filename);
    	     }
  	}
  	return true;
    }
}
// Set the custom PHP session handler
ini_set('session.save_handler', 'user');
session_set_save_handler(array('SecureSession', 'open'),
                         array('SecureSession', 'close'),
                         array('SecureSession', 'read'),
                         array('SecureSession', 'write'),
                         array('SecureSession', 'destroy'),
                         array('SecureSession', 'gc')
);

You can donwload the SecureSession class here.

In order to use the SecureSession class you have to include it in your PHP project before the session_start function.
After that you can use the PHP session as usual.

From zimuel.it

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s