Welcome Guest, Not a member yet? Register   Sign In
Implementing secure controllers/pages in Codeigniter
#1

[eluser]Christopher Imrie[/eluser]
Quote:This is an extract from a blog article I wrote, but brought the code and essential description here in case someone finds it useful

A common need amongst web applications is the need for password protected areas of your web application. Coming up with a good security structure within CodeIgniter can be tricky for those new to the framework since there is no clear method to doing so.

The model I have developed has the following features:

* Supports both secure and unsecure controllers (for public areas of your site)
* No modification of core files
* CodeIgniter library can be upgraded with new releases without breaking the security model
* Un-authenticated URL requests are intercepted, shown a login screen, then redirected to the original URL
* Controllers are kept "security code" free.

Transparency

The method I show you here is very transparent in its implementation. The security is loaded silently in the background and your controllers only need three characters added to them in order to inherit the protection for the security system. Therefore you can work in any CodeIgniter controllers without any extraneous security code getting in the way.

Keeping things as secure as possible

In order for the method I show you to have the best security, make sure you have the following setup:

* Session library is autoloaded (More info)
* Session data is saved to the database (More info)
* Session data is encrypted in your session preferences (More info)

How it works

The system works by extending the CodeIgniter Controller class and having any secure controllers extending from this new class. Therefore make sure that in your preferences you have set your Class Extension Prefix to the following (this is the default, so it probably wont have changed):

Code:
$config['subclass_prefix'] = 'MY_';

This will allow us to have the custom controller loaded automatically by CodeIgniter on each page load.

Custom Controller

Here is the first file, it is a custom controller that extends the default controller file. I have commented it as extensively as I can and pointed out where you need to add your own customized code. You need to save this file as MY_Controller.php and place it in your ./system/application/libraries/ folder.

Code:
<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Custom secure Controller
*
* @package default
* @author Christopher Imrie
*/
class MY_Controller extends Controller {
    
    /**
     * Secure Constructor
     *
     *
     */
    function MY_Controller()
    {  
        parent::Controller();
        
        // When someone successfully logs in, I set a session userdata variable of "logged_in".
        // Here we check if it exists
        if(!$this->session->userdata('logged_in')){

            // ====================
            // = Login has failed =
            // ====================

            // If not logged in, record what url they are attempting to acces and show the login form
            $this->session->set_userdata('REDIRECT' , uri_string());

            // Give the browser an Unauthorized HTTP header
            $this->output->set_status_header('401');

            // Redirect to the login page, change this to your login controller/method
            redirect('login');

        }else{
            
            // =======================
            // = Login has succeeded =
            // =======================
            
            //This is optional, but I have a method that sets up some constants
            $this->_login_init();

        }
    }
    // END MY_Controller method
    
    /**
     * Performs all login functionality such as setting up constants
     *
     * @return void
     * @author Christopher Imrie
     **/
    private function _login_init()
    {
        // I have a model designed to fetch user data.  Since I am logging a user in,
        // I am loading it here and fetching the user's data
        $this->load->model("users");
        
        
        // I call a method here to setup some constants to be available to all secure controllers
        if($userdata = $this->users->get_userdata($this->session->userdata('username')))
        {
            //If we have data, then setup the constants or do special functionality in here
            
            
            // ===========
            // = Example =
            // ===========
            
            /**
            * Logged in users email
            **/
            // define('USERNAME', $userdata->username);


        }else{
            // ===========================
            // = Error fetching userdata =
            // ===========================
            
            //Destroy the session
            $this->session->sess_destroy();

            //Log it
            log_message('error', 'Session error.  Session id not found');
            
            //Show it
            show_error('Session error, please log in again.');
        }

    }
    // END _login_init method
}
// END MY_Controller class

/* End of file MY_Controller.php */
/* Location: ./system/application/libraries/MY_Controller.php */

Creating a secure controller

Now that this controller class has been setup, the act of creating a secure controller is a breeze:

Code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Secure_page extends MY_Controller {
    
    function Secure_page()
    {
        parent::MY_Controller();
        
    }
    
}

/* End of file secure_page.php */
/* Location: ./system/admin/controllers/secure_page.php */

Note the "extends MY_Controller" keywords. This ensures that the security is enforced.
#2

[eluser]Christopher Imrie[/eluser]
Login Controller

For clarity I have also included the login controller that functions as the login screen. This file is to be named login.php and placed in your ./system/application/controllers/ folder.

Code:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Login Controller
*
* @package default
* @author Christopher Imrie
*/
class Login extends Controller {

    /**
     * Login Controller
     *
     *
     */
    function Login()
    {
        
        parent::Controller();    
    
    }
    // END Login method

    function index()
    {
        //Check to see if some key session userdata is still available
        if($this->session->userdata('logged_in') && $this->session->userdata('username'))
        {
            // ===========================
            // = User is still logged in =
            // ===========================
            
            //Dont bother with the login page, just send em on to the start page
            redirect('start');
            
        }else{
            // =========================
            // = User is not logged in =
            // =========================
            
            // Lets use the form validation library to keep things simple & robust
            $this->load->library('form_validation');
            $this->form_validation->set_rules('username', 'Username', 'required|callback__verify_username_password');
            $this->form_validation->set_rules('password', 'Password', 'required');
            
            //We have a custom method that is checking the username & password validity
            $this->form_validation->set_message('_verify_username_password', "Username or password is incorrect");

            if(!$this->form_validation->run())
            {
                // ====================
                // = Login has failed =
                // ====================
                
                //Load up the login screen
                $this->load->view('login_view');
            
            }else{
                // =======================
                // = Login has succeeded =
                // =======================
                
                //Set our custom session userdata to be checked by our secure controller
                $this->session->set_userdata('logged_in' ,TRUE);
                $this->session->set_userdata('username' , $this->input->post('username'));

                
                // =================================
                // = Redirect based on user intent =
                // =================================
                
                //Check if user was trying to access a specific URL before login procedure
                if($this->session->userdata('REDIRECT'))
                {
                    //Save the REDIRECT
                    $redirect = $this->session->userdata('REDIRECT');
                    
                    //Unset the REDIRECT otherwise we end up in a loop
                    $this->session->unset_userdata('REDIRECT');
                    
                    //Away we go..!
                    redirect($redirect);
                    
                }else{
                    
                    // There is no REDIRECT set, just send em to the start page
                    redirect('start');
                    
                }
            }
        }
    }
    // END index method
    
    
    /**
     * Verifies that the username and password are correct
     *
     * @return void
     * @author Christopher Imrie
     **/
    function _verify_username_password($str)
    {
        $username = $str;
        $password = $this->input->post('password');
        
        /*
         * Perform your username and password check here.
         *
         * Fetch stuff from the DB, or do whatever you like as long as you return TRUE/FALSE
         */
        
         //return TRUE / FALSE;
    }
}
// END Login class

/* End of file login.php */
/* Location: ./system/application/controllers/login.php */

This is a very bare bones login controller but it will do the trick very nicely and crucially sets up some session userdata that is checked by the custom controller I showed you above. Simply create a view named login_view.php (or modify the code above to suit your naming structure) and have this show a form with username and password fields. You will also need to fill in the _verify_username_password() method with your own verification code and then return TRUE/FALSE to indicate a successful username & password match.


Hope someone finds this handy, since it allows you to work on your web app with the security being as transparent as possible in your controllers.
#3

[eluser]cryogenix[/eluser]
this is nice. i think i might give it a try sometime.

one thing though (and I don't mean to pop your bubbles), but, why try to reinvent the wheel? aren't there alot of auth solutions here already?

i currently am using ION auth btw. and here's the way I use it: http://ellislab.com/forums/viewthread/149476/#773561

cheers. my 2 cents worth...
#4

[eluser]Christopher Imrie[/eluser]
@cryogenix thanks.

I know what you're saying about reinventing the wheel, totally agree that you will probably hit the ground running much faster if you go with Ion Auth. Especially so when you consider it handles password encryption and user handling.

Just wanted to show that its not that tricky to implement a reasonably secure security model in CI.

I just eventually ended up using this method after many different attempts at a nice easy to use security model, and have since saved it as a standard library to use with all my projects. I've fleshed it out with my own user handling methods and custom password handling, so I know it inside out.




Theme © iAndrew 2016 - Forum software by © MyBB