Back-End

5 jul, 2011

Criptografia de dados de sessão no PHP

Publicidade

Texto original disponível em http://www.zimuel.it/blog/2011/02/encrypt-php-session-data/

?

Este artigo apresenta um exemplo de sólida criptografia no
PHP para proteger os dados de sessão.

É uma implementação bastante simples, que pode ser usada para melhorar a
segurança de aplicações PHP, especialmente em ambientes compartilhados, nos quais diferentes
usuários têm acesso aos mesmos recursos. Como você sabe, as sessões de dados PHP
são gerenciadas, por padrão, usando arquivos temporários. Em um ambiente
compartilhado, um usuário malicioso que é capaz de acessar esses arquivos
temporários pode facilmente ler os dados de sessão, uma vez que eles estão
armazenados em plaintext (dados em um arquivo de sessão são a serialização do
array $_SESSION).

Em
teoria, os dados de sessão deveriam ser armazenados em arquivos que são
acessíveis somente ao proprietário do site, mas nunca diga nunca (inclusive,
você pode gerenciar o local dos dados de sessão usando a função session_save_path
ou mudando o session_save_path no the php.ini).

Para
proteger os dados de sessão, eu usei uma sólida criptografia para criptografar
o conteúdo usando a extensão mcrypt do PHP. Eu escolhi a cifra simétrica AES
(Rijandel-256) para criptografar os
dados de sessão e a função openssl_random_pseudo_bytes() para
gerar uma chave randômica do bit 256.

A ideia é
usar um cookie variável para armazenar a chave que será utilizada para
criptografar os dados de sessão. Dessa maneira, a chave é armazenada somente no
cliente (o browser) e somente o cliente é capaz de descriptografar os dados de
sessão no servidor. Cada vez que criptografamos os dados de sessão, nós geramos
mais uma vez o vetor IV de uma maneira randômica usando a função mcrypt_create_iv().
É muito importante gerar um único
IV em cada criptografia. Essa prática aumenta a segurança do algoritmo de
criptografia. É importante notar
que essa implementação não protege contra um ataque de session hijacking. Se alguém for capaz de capturar
o cookie variável de um cliente e ter acesso aos arquivos temporários da
sessão, no servidor, ele ou ela serão capazes de descriptografar os dados de
sessão. Nosso objetivo é proteger os dados de sessão contra ataques em
ambientes compartilhados.

A ideia de criptografar dados de sessão não é
nova, Chris Shiflett por exemplo, propôs uma
implementação em seu livro “Essential
PHP Security”
(O’Reilly,
2006). Shiflett usou uma variável $_SERVER para armazenar a chave usada para
criptografar os dados de sessão. Kevin
Schroeder
, meu
colega na Zend Technologies, implementou um algoritmo de criptografia da sessão
bem parecido, estendendo a classe Zend_Session class do Zend Framework (você
pode encontrá-lo aqui
). Na
minha solução, eu usei algumas das melhores práticas relacionadas à uma sólida
criptografia para implementar um handler de sessão segura.

Abaixo temos o código-fonte para a minha
implementação:

/**
* ------------------------------------------------
* 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')
);

Você pode
fazer o donwload da classe SecureSession aqui.

Para
poder usar a classe SecureSession, você tem que incluí-la no seu projeto PHP
antes da função session_start.

Depois disso
você pode usar a sessão PHP como de costume.