Dados criptografados na sessão PHP por Enrico Zimuel






Neste artigo, apresento um exemplo de forte criptografia em PHP para dados em uma sessão segura. Essa é uma simples implementação que pode ser usada para melhorar a segurança de aplicações PHP, especialmente em ambientes compartilhados nos quais usuários diferentes têm acesso aos mesmos recursos. Como você sabe, os dados nas sessões PHP são gerenciados 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 esses dados porque eles estão armazenados em plaintext (dados em um arquivo de sessão é a serialização do array $_SESSION).
Em teoria, os dados na sessão deveriam ser armazenados em pastas que somente são acessíveis pelo dono do website, mas nunca diga nunca (falando nisso, você pode gerenciar o local dos dados na sessão usando a função session_save_path ou mudando asession_save_path no php.ini).
Para assegurar a os dados na sessão, eu usei uma criptografia forte para criptografar o conteúdo usando a extensão mcrypt do PHP. Eu escolhei a cifra simétrica Rijandel-256 para criptografar os dados da sessão e a função openssl_random_pseudo_bytes() para gerar uma chave randômica de 256 bit.
A ideia é usar um cookie variável para armazenar a chave que será usada para criptografar os dados na sessão. Dessa maneira, a chave é armazenada somente no cliente (o browser) e somente o cliente é capaz de descriptografar os dados da sessão no servidor. Cada vez que criptografamos os dados da sessão, nós geramos mais uma vez o vetor IV de maneira randômica usando a função mcrypt_create_iv(). É muito importante gerar um único IV em cada criptografia. Essa prática otimizada melhora a segurança do algoritmo criptografado.
É importante notar que essa implementação não é segura contra um ataque de hijacking. Se outra pessoa for capaz de capturar o cookie variável de um cliente e tiver acesso aos arquivos temporários das sessões, dentro do servidor, ele ou ela será capaz de descriptografar os dados da sessão. Nosso objetivo é proteger os dados da sessão contra ataques em ambientes compartilhados.
A ideia de criptografar os dados na sessão não é nova, por exemplo, Chris Shiflett 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 da sessão.Kevin Schroeder, meu colega na Zend Technologies, implementou um algoritimo de criptografia de sessão muito similar estendendo a classe Zend_Session do Zend Framework (você pode encontrá-lo aqui). Na minha solução, eu usei algumas das melhores práticas relacionadas a fortes criptografias para implementar um handler seguro de sessões.
Abaixo está o código-fonte da 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 download 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 da maneira normal.

Postar um comentário

Comente sem faltar com respeito - ;-)

Postagem Anterior Próxima Postagem