Using Sessions In Zend Framework 2 – Part 2

Managing Sessions in ZF2

In this tutorial, we looking further at Sessions in Zend Framework 2, specifically investigating session validators and the different backend storage options available.

In last week’s tutorial we covered the basics of sessions in Zend Framework 2, looking at how to implement them by making changes to module/Application/Module.php so that they’re available application-wide then how to both set and retrieve information in the session.

In this week’s tutorial, we’re going to take last week’s post further, by looking at session validation as well as different backends.

These two things will help protect your session data from session hijacking, as well as help you scale your application, by storing the information using a more universal backend, which a filesystem most often times will never be.

Some Session Background

Before we jump in to looking at validators and save handlers, last week’s post was a bit light on. So I want to cover a bit more about the Session classes. Firstly, using the Zend Framework 2 classes, sessions are able to be namespaced.

ZF2 Session Namespaces

 

In the standard PHP session classes, everything is in one large bucket (if you will). However, a lot like namespace support in PHP, the session data is able to be namespaced as well, by the use of Zend\Session\Container objects which are what store the Session data. This provides for a more logical order of the information contained.

Session Validators

Firstly, let’s look at the session validators. By default, Zend Framework 2 comes with 2: HttpUserAgent and RemoteAddr.

HttpUserAgent: keeps track of the User-Agent string provided by the client, usually the browser, across the requests to the session. Specifically, the classes use the $_SERVER['HTTP_USER_AGENT'] variable. If, at any time, the User-Agent string provided changes, then the request is deemed to be invalid.

This may or may not be a good validator to use. In all likelihood, a User-Agent won’t change often. But then, the people that you’re trying to protect yourself from, won’t be your average, normal, users.

RemoteAddr: keeps track of the remote IP address used by the client when interacting with the session. As with HttpUserAgent, if at any time it changes, then this is deemed to be an invalid request.

However, as the code comments state (Zend\Session\Validator\RemoteAddr.php), this isn’t necessarily a reliable check to use, as it can be easily spoofed, plus it can be prone to error if the user is sitting behind a proxy.

However, depending on your situation, it may be a worthwhile class to use. To enable both of these, I’ll update the example from last week, as below:

$this->initSession(array(
  'remember_me_seconds' => 180,
  'use_cookies' => true,
  'cookie_httponly' => true,
  'validators' => array(
    'Zend\Session\Validator\RemoteAddr',
    'Zend\Session\Validator\HttpUserAgent',
  ),
));

Alternatively, we could have configured them this way, by updating the initSession function:

public function initSession($config)
{
  $sessionConfig = new SessionConfig();
  $sessionConfig->setOptions($config);
  $sessionManager = new SessionManager($sessionConfig);
  $manager->getValidatorChain()
          ->attach(
            'session.validate',
            array(new HttpUserAgent(), 'isValid')
          )
          ->attach(
            'session.validate',
              array(new RemoteAddr(), 'isValid')
          );
  $sessionManager->start();
  Container::setDefaultManager($sessionManager);
}

By adding in the validators array, along with the two validator classes, we’ve now enabled support for both of the validators above. Nothing too hard.

Session Storage Backends

As with the Session validators, Zend Framework 2 comes with some pre-packaged storage backends; these being Cache, DbTable and MongoDB.

In the documentation, you’ll see examples of configuring each of these three, so I won’t try and rehash them here. But I will further refactor initSession, so that we have a living example:

public function initSession($config)
{
  $sessionConfig = new SessionConfig();
  $sessionConfig->setOptions($config);
  $sessionManager = new SessionManager($sessionConfig);
  $manager->getValidatorChain()
          ->attach(
          'session.validate',
            array(new HttpUserAgent(), 'isValid')
          )
          ->attach(
            'session.validate',
            array(new RemoteAddr(), 'isValid')
          );

  $cache = StorageFactory::factory(array(
    'adapter' => array(
    'name' => 'memcached',
    'options' => array(
    'server' => '127.0.0.1',
  ),
)
));

$saveHandler = new Cache($cache);
$sessionManager->setSaveHandler($saveHandler);
$sessionManager->start();

Container::setDefaultManager($sessionManager);

}

Wrapping Up

So there you have it. With not a lot of effort, we’ve seen how to quickly and simply extend an existing Session configuration in Zend Framework 2 so that we can add in support for session validation as well as use a variety of different session save handlers.

How will this enable you to write more flexible and scaleable applications?

Related Files:

  • Zend\Session\Validator\RemoteAddr
  • Zend\Session\Validator\HttpUser Agent
  • Zend\Session\SaveHandler\Cache.php
  • Zend\Session\SaveHandler\DbTableGateway.php
  • Zend\Session\SaveHandler\MongoDB.php
Sessions
  • razor7_996

    Just wondering if it is safe to turn cookie_httponly to false…Is there any security risk?

    • http://www.matthewsetter.com/ Matthew Setter

      Hi razor7_996 I’ll add this excerpt from http://forums.phpfreaks.com/topic/263186-do-i-understand-sessioncookie-httponly-secure/.

      > session.cookie_httponly and session.cookie_secure when set in php.ini or using ini_set will ensure that the session id cookie is only accessible through HTTP (and not JavaScript) and also that it is only sent when using a https connection.

      > They would see nothing, and/or an entire encrypted data stream. The cookies are transmitted along with the page itself, not as separate documents, so the entire page request contains headers, which may or may not contain cookies. Since the entire transmission is encrypted due to the secure flag, they can only tell “some data is being sent.”

      So it really depends on what information you’re storing.

      • razor7_996

        Thanks! and great site! congrats!

        • http://www.matthewsetter.com/ Matthew Setter

          @razor7_996:disqus I’m really glad you like it. If there’s anything else that you want to see covered, tweet me @zfmastery. Happy to write about what you want to know.

  • RR

    It would be nice if you could show sessions in action within controllers and in a login and log out scenario.

    • http://www.matthewsetter.com/ Matthew Setter

      Sorry to take so long to respond. I’ll see what I can do about writing up an example for you.

  • Dan

    Hi
    This example is not working

    • http://www.matthewsetter.com/ Matthew Setter

      Dan, I’ll revisit the example and post a correction if need be. Thanks for mentioning it.

  • Habibi

    Hi Matthew,
    shouldn’t the validators be added after $sessionManager->start() so their data could be saved to the Session and be used for validating on next start or do I miss something?

    • http://www.matthewsetter.com/ Matthew Setter

      Thanks for asking. I don’t believe so, but I’ll check the example and make a change if needs be. I’m quite intrigued now.

  • Thiago Souza Dias

    Hi Matthew!
    How can I use sessions on multiple modules?

    • http://www.matthewsetter.com/ Matthew Setter

      Hi Thiago, in Zend Framework 2 module’s only really exist from a development perspective. At runtime all the active modules effectively become one, if you take my meaning. So if one module implements session support, then the others can take advantage of it. For example, you could create 2 modules, one that provides session support and one that uses it or one that provides your core classes and then a frontend and backend module which use that core module. Does that help?

      • Thiago Souza Dias

        Thanks Matthew, you save me.

  • Gareth

    This doesn’t work at all, if you’re going to have an example of code, please make it functional

    • http://www.matthewsetter.com/ Matthew Setter

      Sorry to hear it isn’t working for you Gareth. What was the error you encountered?