Ole Haugset Ole Haugset - 21 days ago 6
PHP Question

Good practice to protect / limit functions to user groups

I have a big project extending over multiple class files. This project was made in a hurry to meet a deadline. This took its toll on the projects security. So to put it simple, anyone could in theory
call an AJAX script in my project and have it run since the functions in the scripts are not user privileges aware. Of course I do a check in all my AJAX PHP scripts to see whether a user is logged in or not.

So here is an example:

// Update user information. This script is called with jQuery AJAX:
<?php
include('../../../config/config.php');
require_once('classes/Autoloader.php');

$iris = new IRIS();

if( !$iris->UUID() ){
die( "Unauthorized access! ");
}

$result = array('success'=>true);

$users = new users();

try {
$res = $users->createUser();
$result = array( 'success' => true, 'result' => $_POST );
}
catch(Exception $e){
$result = array( 'error' => true, 'errmsg' => $e->getMessage() );
}

echo json_encode( $result );


I have 3 user classes: "Admin", "client", "employee". Now in theory, all of these could call this php script, and modify users, even if its just admins who should have access to do it. Users have specified privileges parameters in the database, which I define by a constant in my project:

define('USER_AUTH_LEVEL', $_SESSION['auth_level']);


I am aware that I could update every AJAX PHP file and set:

if( USER_AUTH_LEVEL !== 1 ) { die( 'You do not have access' ); }


But what I want to do, is to protect all, or some of my classes' functions as well. Let's say:

class IRIS {
public function doSomething(){
// $function_access not defined, accessable by all
}
public function doSomethingAdmin(){
$function_access = 1;
// Should be limited to admins
}
public function doSomethingClient(){
$function_access = 2;
// Should be limited to clients
}
}


What I would like to achieve is a script that runs every time a function, no matter the class it is in is run. This script should check to see if the parameter
$function_access
is set. If it is, it should compare to the privileges level of the currently logged in user. If it does not match, the function should break. If it is not defined at all, anyone can run the function.

Does anyone have an approach to this? I'm totally blank here.

Answer

You need to call some function before any function call. To do so, use __call method:

class IRIS extends Access {
    /* other methods here */

    // list all methods to allow some access levels.
    public function accessControll() {
       return [
          parent::ADMIN => ['doSomethingAdmin', 'doSomethingElseAdmin'],
          parent::USER => ['doSomething', 'doSomethingElse'],
       ];
    }
}

abstract class Access {
    // access levels defines
    const ADMIN = 1, USER = 2;

    public function __call($method, $arguments) {
        $accessLevel = -1;

        // get access level of current method
        foreach ($this->accessControll() as $key => $group) {
            if (in_array($method, $group)) {
                $accessLevel = $key;
                break;
            }
        }

        if (USER_AUTH_LEVEL !== $accessLevel) {
            throw new Exception('You do not have access');
            // OR header('HTTP/1.0 401 not authorized');
        }
    }

    // abstract, so every class, that extends it must have this method.
    abstract public function accessControll();
}