Hi list, first time poster here so please be gentle ;-)
I'm getting up and running with Zend Framework for the first time and
have googled and read documentation profusely, and have a working
authentication setup, but I wanted to post it to this list and ask one
question: is this secure?
There's one hole I'm aware of that I'll explain below, but I want to be
sure that my implementation - specifically, my use of init() to ensure
the user is authenticated for all actions in the controller - is
considered within "best practices". If not, I need to know what I
should be doing instead.
The way this works is that there is ONE and only one user in the users
table in the database. It's got a username, password, and of course ID
- that's it. The password is stored as an MD5 checksum in the password
field.
When the "admin" controller is accessed, I want the very first thing
that happens to be a check to see if the user that's logged in is
authenticated. If not, it should redirect to the login form and
terminate immediately. I believe the use of init() takes care of this,
but let me know if I'm missing something here. Should I be using
__construct instead, for example?
The only hole I can see with this implementation is if somebody gained
access to my database, for any reason, and inserted another row into the
admin table, they could login and have the same control over this that I
do. I'll implement other controls to restrict that of course, but I
want to be sure the direction I'm taking here is solid before I get too
deep.
Enough BS, here's the code - please let me know what you think.
- Phoenix
AdminController.php:
-----------------
<?php
class AdminController extends Zend_Controller_Action
{
public function init()
{
$this->view->title = "Admin Controller";
$this->_helper->layout->setLayout('admin');
// Authentication
$auth = Zend_Auth::getInstance();
if(!$auth->hasIdentity()) {
// User is not authenticated, so return a redirect.
return $this->_helper->redirector("index", "login");
}
}
// All my other actions down here, none of which can be accessed without
going through init(), right?
LoginController.php
-------------------------
<?php
class LoginController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->form = $this->getForm();
}
public function getForm()
{
require_once(FORMS_PATH.'/loginform.php');
return new LoginForm(array('action' => '/login/process',
'method' => 'post'));
}
public function preDispatch()
{
if(Zend_Auth::getInstance()->hasIdentity()) {
if('logout' != $this->getRequest()->getActionName()) {
$this->_helper->redirector('index', 'admin');
}
}
else {
if('logout' == $this->getRequest()->getActionName()) {
$this->_helper->redirector('index');
}
}
}
public function processAction()
{
$request = $this->getRequest();
// If this ISN'T a post request, redirect and die
if(!$request->isPost()) {
return $this->_helper->redirector('index');
}
// Retrieve and validate form
$form = $this->getForm();
if(!$form->isValid($request->getPost())) {
// Invalid form entries
$this->view->form = $form;
return $this->render('index'); // re-render the login form
}
// Set up $username and $password
$username = $form->getValue('username');
$password = $form->getValue('password');
// Get authentication adapter and check credentials.
require_once(MODELS_PATH.'/auth.php');
$adapter = new AuthAdapter($username, $password);
$auth = Zend_Auth::getInstance();
$result = $auth->authenticate($adapter);
// Test to see if they're authenticated or not
if(!$result->isValid()) {
// NOT authenticated
$form->setDescription("Your login failed.");
$this->view->form = $form;
return $this->render('index');
}
// If we made it past all the if/return combinations, we're
authenticated
$this->_helper->redirector('index', 'admin');
}
public function logoutAction()
{
Zend_Auth::getInstance()->clearIdentity();
$this->_helper->redirector('index', 'index');
}
}
And finally, my authentication class: auth.php
-----------------------------------------
<?php
class AuthAdapter implements Zend_Auth_Adapter_Interface
{
/*
* Sets username and password for authentication
*/
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}
/*
* Performs an authentication attempt
*
* @throws Zend_Auth_Adapter_Exception If authentication cannot be
performed
*
* @return Zend_Auth_Result
*/
public function authenticate()
{
// set up authentication adapter
$adapter = new Zend_Auth_Adapter_DbTable($this->getDB());
$adapter->setTableName('admin');
$adapter->setIdentityColumn('user');
$adapter->setCredentialColumn('pass');
// Set the actual credentials themselves
$adapter->setIdentity($this->username);
$adapter->setCredential($this->password);
$adapter->setCredentialTreatment("MD5(?)");
// Perform the authentication and trap the results in an object.
$result = $adapter->authenticate();
return $result;
}
private function getDB()
{
return Zend_Registry::get('db');
}
}