Is this session handler secure? Are there any security vulnerabilities in this code?



class Session {
    private $id = array();
    private $data = array();
    private $name = 'session';

    public function __construct() {
        session_start();

        global $db;

        $this-&gt;id = isset($_COOKIE[$this-&gt;name]) ? $_COOKIE[$this-&gt;name] : $this-&gt;create_id();

        $id = $this-&gt;id;
        $ip = $_SERVER['REMOTE_ADDR'];
        $user_agent = $_SERVER['HTTP_USER_AGENT'];

        $check = $db-&gt;query("SELECT session_data FROM sessions WHERE session_id = ? AND user_ip = ? AND user_agent = ?");
        $db-&gt;execute(array($id, $ip, $user_agent));

        if ($db-&gt;row_count() != 0) {
            $this-&gt;data = $db-&gt;fetch_column();
            $this-&gt;data = unserialize($this-&gt;data);
        } else {
            $this-&gt;id = $this-&gt;create_id();

            $id = $this-&gt;id;
            $data = serialize($this-&gt;data);
            $time = time();
            $ip = $_SERVER['REMOTE_ADDR'];
            $user_agent = $_SERVER['HTTP_USER_AGENT'];
            $current_page = $_SERVER['REQUEST_URI'];

            $insertion_data = array(
                'session_id'                =&gt;      $id,
                'session_data'              =&gt;      $data,
                'session_last_activity'     =&gt;      $time,
                'user_ip'                   =&gt;      $ip,
                'user_agent'                =&gt;      $user_agent,
                'user_current_page'         =&gt;      $current_page
            );

            $db-&gt;insert('sessions', $insertion_data);
        }
    }

    public function end() {
        global $db;

        $this-&gt;garbage();
        $id = $this-&gt;id;
        $data = serialize($this-&gt;data);
        $time = time();
        $ip = $_SERVER['REMOTE_ADDR'];
        $user_agent = $_SERVER['HTTP_USER_AGENT'];
        $current_page = $_SERVER['REQUEST_URI'];

        $name = $this-&gt;name;

        // note to self: create update function in db class
        $db-&gt;query("UPDATE sessions SET session_data = ?, session_last_activity = ?, user_ip = ?, user_agent = ?, user_current_page = ? WHERE session_id = ?");
        $db-&gt;execute(array($data, $time, $ip, $user_agent, $current_page, $id));

        @setcookie($name, $id, time()+3600, '/', '');
    }

    public function create_id() {
        return strtoupper(md5(rand(-1000000, 1000000)));
    }

    public function garbage() {
        global $db;

        $expiration = time() - '2419200';

        $db-&gt;query("DELETE FROM sessions WHERE session_last_activity &lt; ?");
        return $db-&gt;execute(array($expiration));
    }

    public function regenerate() {
        global $db;

        $old_id = $this-&gt;id;
        $new_id = $this-&gt;create_id();

        $this-&gt;id = $new_id;

        $db-&gt;query("UPDATE sessions SET session_id = ? WHERE session_id = ?");
        return $db-&gt;execute(array($new_id, $old_id));
    }
}
Nope. Let's walk through it from a hypothetical attackers point of view.


  Okay. It's sending an identification cookie. It's hex. 32 digits long.
  Probably an md5 hash. I wonder what's hashed? I'll throw it in
  oclHashcat to check. I can make a billion guesses a second, so if it's
  a hash of something simple, I'll find out.
  
  &lt;3 seconds later>
  
  Huh. It's a number. Lemme grab another session and see if that's a
  number too.
  
  Yep.
  
  It looks like these are all 6 digit numbers. I'll grab 10 more to
  check. 
  
  Okay, 6 digits AND they can be positive or negative.
  
  That's a keyspace of about 2^21. If there are 8 sessions going right
  now, then it costs, on average,
  
  2^(21 - 1 - 3) requests * 1 KB per request * 0.08 $/GB
  ...
  =1.01 cents to crack a session.
  Alright, I'll start on that and see what I have in the morning.


So: Add more entropy.