Base Controller

If everything worked correctly and you have a working application, pat yourself on the back. Let's talk a second about security and then, as a bonus, we will extend this a little bit and introduce a new component.

As it sits right now, anyone with a copy of curl or wget could hammer your little application until either your server couldn't take it or Yahoo! cut you off; neither of which are good things. To prevent this from happening we need to introduce the concept of a token. If you are familiar with this concept, skip ahead to the section on implementation.

A token is simply a piece of information that the form recognizes and the server recognizes. To be effective, it has to be hard to guess. We generate the token on the server site, store it in the session and in the form as a hidden field. Then when the form comes back, we compare the value to what is stored in the session. If they don't match, we know something is wrong and we abort the form. It sounds simple and truly, it is. Let's look at how to implement this.

In this example, we will build a simple and "reasonably secure" token. It's an md5 hash of the current server time plus a secret that only the application knows. In our example, the secret is the word "book". I don't recommend using something this simple in a commercial application, but as example code, it will work.

Before we dive in, let's talk about how, and more importantly where we want to implement this. The obvious answer on where to implement the code to generate and checkthe token is in the IndexController. However, if you have multiple controllers, you will find yourself re-implementing this code multiple times. In the OO world, the term for that is "bad code". Abetter solutionis to create a BaseController to house all of these common functions that multiple controllers will need to access. (Actually, the best solution would to be create a TokenManager class that all parts of the system could use. But that wouldn't let me showyou how to create a BaseController, so work with me here.)

In our simple case here, the BaseController will only contain the code necessary to deal with tokens.

The BaseController we are building here is a forced example. In a production system the two methods we are placing in the BaseController, would be better off as action helpers. Our application is simple enough so that a BaseController is not strictly necessary. I have found however, that it is easier to create one in the beginning, even if I don't absolutely need it as opposed to finding that I need one well into the coding phase of the project. This is one of those "Do as I say, not as I do" examples; don't try this at home.

Create afile, controllers/BaseController.php. If you don't want to take the time to do it yourself, all this code is in example2.zip.

ZencLLoader: : loadClass( 'Zend_Cont roller_Action'); Zend_Loader::loadClass('Zend_Session_Namespace');

First, we move the loadClass for the Zend_Controller_Action into this since this class now subclasses it and not the IndexController. Second, let's load up the Zend_Session_Namespace. We will need it for storing the token.

class BaseController extends Zend_Controller_Action {

protected function generateToken($seed='book') {

$globalSession = new Zend_Session_Namespace('global_data'); $globalSession->token = $token; return $token;

This function generates and stores the token. As you can see, you can specify a seed to add to the mktime() function; however, if you don't it will use the default, in this case, "book". We take the seed, add the value that mktime() returns to us and take an md5 hash of that. While this is by no means foolproof, it's secure enough for most applications.

Once generated, we use the Zend_Session_Namespace to create or open a namespace global_data. If we were programming straight procedural PHP, the equivalent of this command would be $_SESSION['global_data']=array();. It is possible to use both the Zend_Session_Namespace and $_SESSION to manipulate the $_SESSION superglobal directly. However, this is not recommended. With the Zend_Session_Namespace, we get a nice OO wrapper for the PHP session. Finally, the function returns the newly generated token to the calling code. As you will see, we take that return value and hand it off to the view for storage in the form itself.

protected function tokenCheck($tokenToCheck='') {

$globalSession = new Zend_Session_Namespace('global_data');

$returnValue = (!empty($tokenToCheck) and $tokenToCheck==$globalSession->

token); return $returnValue;

This function allows us to check a token against the stored value. Again, we instantiate a Zend_Session_Namespace with the namespace global_data. We are actually checking for two different conditions here, failing either will cause the function to return false. First we want to know that the token passed in is not empty. An empty token is invalid by definition. No token==no form. Second, we check to make sure that the token passed in exactly matches the token we stored in the session. Any discrepancy will again cause the function to return false.

Now, let's look at how we use this. You've already seen the IndexController above and for the most part, that does not change. Let's just look at the changes.

First, we now have something to put in the indexAction(). Modify the following lines in your IndexController.php file:

public function indexAction() {

$this->view->token = $this->generateToken(); $flash = $this->_helper->getHelper('flashMessenger'); if ($flash->hasMessages()) {

$this->view->message = implode("<br />", $flash->getMessages()); } // if ($flash->hasMessages()) } // public function indexAction()

We first ask for a new token and store it in the view. Then we use one of the standard view helpers, the flash messenger. We will cover view helpers in the view chapter, but for now just know that there's no need to reinvent a message passing mechanism. Zend Framework comes with one built in. Here you see that we check to see if any messages have been stored in the flashMessenger. If so, we load them into the view. Since we don't want to have to deal with the array in the view, let's just build a string and hand that to the view for display.

Now, add this code to the top of extractAction():

public function extractAction() {

$token = Zend_Filter::get($this->getRequest()->getPost('token'), 'StripTags' );

* Execute the token check */

$this->_helper->flashMessenger->addMessage("I'm sorry but I think there has been an error. Please try again."); $this->_redirect('/index/index'); return false;

$this->generateToken();

Since the token is part of the form, we can retrieve it like any other piece of form data. As always, we sanitize the string before use.

Then we hand it off to tokenCheck() to evaluate. It should be noted at this point that I am normally a vindictive programmer. In situations like this I don't normally issue any message to the user because if the tokenCheck() fails, the chances are good that someone is doing something they shouldn't. Normally, I would simply fail silently and let them try to figure out what is going wrong. However "vindictive" isn't what we normally consider a programming "Best Practice" so I won't teach you that. If the check fails, we add a message into the flashMessenger telling the user that something is wrong. I'm intentionally vague because if someone is trying to abuse the system I don't want to give them too many clues as to what they are doing wrong.

The last line should have caught your eye. If it didn't then either you already know why it's there or you are not paying attention. After we check the token and it's good, we ask for another token. This time we don't save off the return value because we don't care. What we do care about is that the token we just validated can't be used again. Calling generateToken() here causes a new token to be stored in the session. Subsequent calls to extractAction() with the token we already have will immediately fail.

Finally, let's get to the HTML. In index.phtml, you need to add this directly below the <body> tag:

echo "<div id='message'>".$this->message."</div>";

This will take care of displaying the message if there is one. Add this line directly below the <form> tag:

<input type="hidden" id="token" name="token" value="<?PHP echo $this->token;?>" />

This actually puts the token into the form.

That's it, you've now thwarted most of the script kiddies and the idly curious.

Was this article helpful?

0 0

Post a comment