title: Secure cookies in PHP sessions
body: <p>I have developed a PHP session class and tested it using a few examples (see source code below). It appears to be okay. Now I would like to make this session somehow "secure". I found some sample code which is meant to encrypt a cookie (in Courioso's book Expert PHP and MySQL). Here is this code snippet.</p>
  
  <p>Code for encrypted cookie</p>
  
  <pre><code>$cookieData = serialize($user);
  
  $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
  srand();
  $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
  $encryptedData = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $secret, 
              $cookieData, MCRYPT_MODE_CBC, $iv);
  setcookie('user', base64_encode).':'.$iv);
  </code></pre>
  
  <p>and a similar code for decrypting the cookie</p>
  
  <p>My first question is: can I trust this code. I.e. is it really secure?
  (I tried some other code samples of that book and found them rather buggy).</p>
  
  <p>I do not fully understand how the code for encrypting a cookie could be embedded into my session class (either the code above - or any other cookie encryption code). Should I replace the cookie which is automatically generated by PHP (PHPSESSID) or should generate a new cookie and encrypt this custom cookie? (My guess is the second is true, but I would like to be sure).</p>
  
  <p>How is the protection via encrypted cookies meant to work? I imagine the following attack:
  (1) if the attacker get somehow the cookie in his hand he just replies with the same cookie. Okay. he does not have the password but he has the encrypted password. Later I check  only if the encrypted password is okay. So if the attacker has the cookie: game over - no matter if the cookie contains the password or "only" the encrypted password. Correct?
  (2) Okay this problem can be solved using a cyphered transmission like SSL. But then I think if I use SLL than the transmission is anyway cyphered. I don't need to cypher the password a second time using the encryption function. Correct?</p>
  
  <p>Here is the code for the session class I have already.</p>
  
  <p>Thanks</p>
  
  <pre><code>&lt;?php
  
  class SessionClass {
  
      private static $_instance;
      public static function getInstance() 
      {
          if (!(self::$_instance instanceof self))
          {
              self::$_instance = new self();
          }
          return self::$_instance;
      } // getInstance
  
  
      public function __construct()
      {
          session_set_save_handler(
              array($this, "open"), array($this, "close"),
              array($this, "read"), array($this, "write"),
              array($this, "destroy"), array($this, "gc")
          );
  
          $createTable = "CREATE TABLE IF NOT EXISTS `session`( ".
                              "`sessionID` VARCHAR(128), ".
                              "`data` MEDIUMBLOB, ".
                              "`timestamp` INT, ".
                              "`ip` VARCHAR(15), ".
                              "PRIMARY KEY (`sessionID` ), ".
                              "KEY (`timestamp`, `sessionID`))";
  
          mysql_query($createTable);
      } // construct
  
  
      public function __destruct() {
          session_write_close();
      }
  
      public function open ($path, $id) {
          // do nothing
          return (true);
      }
  
      public function close() {
          // do nothing
          return (true);
      }
  
      public function read($id) 
      {
          $escapedID = mysql_escape_string($id);
          $query = sprintf("SELECT * FROM session WHERE sessionID = '%s'", $escapedID);
          $res = mysql_query($query);
  
          if ((!$res) || (!mysql_num_rows($res))) {
              $timestamp = time();
              $query = sprintf("INSERT INTO session (sessionID, timestamp) VALUES ('%s', %s)", $escapedID, $timestamp);
              mysql_query($query);
              return '';
          } elseif (($row = mysql_fetch_assoc($res))) {
              $query = "UPDATE session SET timestamp = ";
              $query .= time();
              $query .= sprintf (" WHERE sessionID = '%s'", $escapedID);
              mysql_query($query);
              return $row['data'];
          } // elseif
  
          return "";
      } // read
  
  
      public function write($id, $data)
      {
          $query = "REPLACE INTO session (sessionID, data, ip, timestamp) ";
          $query .= sprintf("VALUES ('%s', '%s', '%s', %s)", 
              mysql_escape_string($id), mysql_escape_string($data),
              $_SERVER['REMOTE_ADDR'], time());
          mysql_query($query);
          return (true);
      } // write
  
  
      public function destroy($id)
      {
          $escapedID = mysql_escape_string($id);
          $query = sprintf("DELETE FROM session WHERE sessionID = %s", $escapedID);
          $res = mysql_query($query);
          return (mysql_affected_rows($res) == 1);
      } // destroy
  
  
      public function gc($lifetime)
      {
          $query = "DELETE FROM session WHERE ";
          $query = sprintf("%s - timestamp &gt; %s", time(), $lifetime);
          mysql_query($query);
          return (true);
      } // gc
  
  } // SessionClass
  </code></pre>