使用数组保护PHP函数的安全


Secure PHP functions with array?

我目前正在创建一个小型PHP站点,该站点将具有多个不同的访问级别。有些人可以访问所有部分(a、b、c、d),有些人只能访问特定部分(如b、d)。

目前,我有用户登录,并根据mysql数据库中存储的凭据验证用户名/密码。此外,在登录时,我会创建一个数组,列出用户可以访问的所有函数。然后,我将数组存储在他们的会话变量中。每个PHP页面的顶部都有以下代码,

   session_start();
if (!isset($_SESSION["is_auth"])) {
  header("location: login.php");
    exit;
   }

我的问题是,如何限制对功能/页面的访问?我应该让每个函数再次调用数据库,并检查用户是否可以访问该函数,或者只验证特定函数是否在存储在用户会话中的数组中就足够了。

谢谢你的帮助!

会话内容可以被认为是安全的,因为客户端无法访问它。

如果您的登录过程设置了$_SESSION选项,则可以选中此选项。

session_start();
if (!isset($_SESSION["is_auth"])) {
    header("location: login.php");
    exit;
}
// empty() matches to bool(false) and int(0), but throws no notice if index is undefined.
if (empty($_SESSION["allowed_area"]["area_a"])) {
    header("location: access_denied.php");
    exit;
}

您不需要在每个页面上检查数据库中的用户凭据,因为会话变量是安全的。

但你可能会在一段时间后检查它,因为假设你在管理面板中删除了一个用户、锁定了他的帐户或更改了他的密码,但该用户还没有登录。因此,在这种情况下,如果你不在数据库中进行验证,用户将不会被注销,他将被登录,直到他关闭浏览器(他可能不会故意关闭浏览器以继续登录你的网站),你应该注意这种可能性。

第二个问题是会话劫持,如果有人获得了你的有效用户的会话cookie,他也会使用你的网站。因此,您应该在每个页面上使用session_regenerate_id(true);,以便每次将新的会话id分配给用户并删除前一个会话id。

关于如何在服务器的任何资源上授予任何用户的访问权限,我所做的是将所有数据保存在会话上,然后在每个用户请求中,如果用户(或者更确切地说,用户的角色)有足够的权限访问该资源,则在数据库中查找。

我建议使用一个框架作为代码点火器(学习它的工作原理非常容易,有成千上万的教程),并且有一个由Ben Edmunds制作的库调用ion auth。它有登录、注册(如果你配置了-,可以自动发送电子邮件激活帐户)、恢复密码,以及一些访问用户的安全功能。

对于权限,我使用的是这个库:

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Permissions
{
    public function __construct()
    {
    }
    public function __call($method, $arguments)
    {
        if (!method_exists( $this->ion_auth_model, $method) )
        {
            throw new Exception('Undefined method Ion_auth::' . $method . '() called');
        }
        return call_user_func_array( array($this->ion_auth_model, $method), $arguments);
    }
    public function __get($var)
    {
        return get_instance()->$var;
    }
    public function control(){
        // controla la session
        if(!$this->session->userdata('id')) redirect(base_url("/login"), "refresh");    
        // valida los permisos
        $group = $this->permissions_model->lookUpGroup($this->session->userdata('user_id'));
        $menu = $this->permissions_model->lookUpPermission($this->uri->segment(1), $group->group_id);
        //echo "url: ".$this->uri->segment(1)." grup: ".$group->group_id."<br>";
        //var_dump($menu);
        //die();
        if((!$menu || empty($menu)) || $menu->read == 0 && $menu->menu_id != 12 ){
            redirect(base_url("/dashboard"), "refresh");
        }           
        // retorna permisos
        $menu = $this->permissions_model->getByLink($this->uri->segment(1));
        return $permisos = $this->permissions_model->permissions($menu->id, $this->session->userdata('user_id'));
    }
    public function getMenu(){  
        $ci = &get_instance();
        if($ci->session->userdata("id"))
        {
            $user_id = $ci->session->userdata("id");
            $father_query = "SELECT  u.id usuario, ug.user_id, ug.group_id, p.menu_id, p.read, p.insert, p.update, p.delete, p.group_id, m.*
            FROM users u
            JOIN users_groups ug        ON u.id = ug.user_id
            JOIN permisos p             ON ug.group_id = p.group_id
            JOIN menus m                ON p.menu_id = m.id
            WHERE u.id = ".$user_id."   AND m.active=1
            AND m.parent=0
            ORDER BY m.orden
            ";
            $query_padres = $ci->db->query($father_query);
            if($query_padres->num_rows()>0)
            {       
                foreach($query_padres->result() as $field)
                {
                    $consulta_hijos = "SELECT  u.id usuario, ug.user_id, ug.group_id, p.menu_id, p.read, p.insert, p.update, p.delete, p.group_id, m.*
                    FROM users u
                    JOIN users_groups ug        ON u.id = ug.user_id
                    JOIN permisos p             ON ug.group_id = p.group_id
                    JOIN menus m                ON p.menu_id = m.id
                    WHERE u.id = ".$user_id."   AND m.active=1
                    AND m.parent=".$field->id."
                    AND p.read=1
                    ORDER BY m.orden";
                    $sub_query = $ci->db->query($consulta_hijos);
                    if($sub_query->num_rows()>0){
                        $submenus = '';
                        $active_menu = '';
                        $active_menu_collapse = '';
                        foreach ($sub_query->result() as $result_dos) {
                                    if ($ci->uri->segment(1) == $result_dos->link && $ci->uri->segment(2) == $result_dos->sublink) {
                                        $submenus .= "<li class='active'><a href='". base_url($result_dos->link."/".$result_dos->sublink)."'><i class='".$result_dos->iconpath."'></i> ".$ci->lang->line($result_dos->descripcion)."</a></li>";
                                        $active_menu = 'active';
                                        $active_menu_collapse = 'collapse in';
                                    }else{
                                        $submenus .= "<li><a href='". base_url($result_dos->link."/".$result_dos->sublink)."'><i class='".$result_dos->iconpath."'></i> ".$ci->lang->line($result_dos->descripcion)."</a></li>";
                                    }
                        }
                        echo    "<li class='".$active_menu."'>
                                    <a href='javascript:;'><i class='".$field->iconpath."'></i><span class='title'>".$ci->lang->line($field->descripcion)."</span><span class='arrow'></span></a>
                                    <ul class='sub-menu'>
                                    ".$submenus."
                                    </ul>
                                </li>";
                    }
                    else if (($ci->uri->segment(1) == $field->link && $ci->uri->segment(2) == $field->sublink) || $ci->uri->segment(1) == '') {
                        echo "  <li class='active'>
                                    <a href='".base_url($field->link."/".$field->sublink)."'>
                                        <i class='".$field->iconpath."'></i> 
                                        <span class='title'>".$ci->lang->line($field->descripcion)."
                                        <span class='selected'></span>
                                    </a>
                                </li>";
                    }else{
                        echo "<li>
                                <a href='".base_url($field->link."/".$field->sublink)."'>
                                    <i class='".$field->iconpath."'></i> 
                                    <span class='title'>".$ci->lang->line($field->descripcion)."</span>
                                    <span class='selected'></span>
                                </a>
                            </li>";
                    }
                }
            }
        }
    }
}

在这之后,我在核心文件夹上创建了一个父控制器(从中扩展您想要保证正确访问的每个控制器)。核心文件夹上的控制器示例:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Main_Father extends CI_Controller {
    public function __construct() {
        parent::__construct();
        //session_start();
        $this->load->helper('url');
        if($this->getPermissions())
        {
            //make some things before pass control to the authentic controller
        }
        else 
            redirect(base_url());
    }
    private function getPermissions()
    {
        $this->user = $this->ion_auth->user()->row();
        if($this->user)
        {
            //$this->data["permissions"]    = $this->permissions->control();
            if($this->permissions->control())
            {
                //make any function
                return true;
            }
            else return false;
        }
        else
        {
            if(strpos(base_url(uri_string()),"login")==false)
                redirect(base_url('/login'.$url));
            else return false
        }
    }
}

最后一个元素。。。Permission_model

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Permissions_model extends CI_Model {
    function __construct() {
        parent::__construct();
    }

    function get_all(){
        $query = $this->db->get('groups');
        return $query->result();
    }
    function getAll(){
        $user_row = $this->ion_auth->user()->row();
        if (!$this->ion_auth->in_group(1, $user_row->id)){
            $query = $this->db
                ->select('p.*, g.name AS grupo, m.descripcion AS menu')
                ->join('groups g','p.group_id = g.id')
                ->join('menus m', 'p.menu_id = m.id')
                ->where('p.group_id !=', 1)
                ->get('permisos p');
        }else{
            $query = $this->db
                ->select('p.*, g.name AS grupo, m.descripcion AS menu')
                ->join('groups g','p.group_id = g.id')
                ->join('menus m', 'p.menu_id = m.id')
                ->get('permisos p');      
        }
        return $query->result();
    }
    //buscar
    function lookUp(/*$perpage,$start*/){
        $query = $this->db
            ->select('permisos.id AS id,
            menus.descripcion AS menu,
            groups.id AS id_grupo,
            groups.description  AS grupo,
            permisos.read AS Leer,
            permisos.insert AS Insertar,
            permisos.update AS Actualizar,
            permisos.delete AS Borrar,
            permisos.exportar AS Exportar,
            permisos.imprimir AS Imprimir')
            ->join('menus',     'menus.id = permisos.menu_id', 'inner')
            ->join('groups',    'groups.id = permisos.group_id', 'inner');
        if($where = $this->input->post('buscar', TRUE))
            $query = $this->db->where('permisos.id =', $where)
                ->or_where("menus.descripcion LIKE '%".$where."%'")
                ->or_where("groups.description LIKE '%".$where."%'");
        $query = $this->db
            ->get('permisos');
        return $query->result('array');
    }
    //count_buscar
    function count_lookUp($perpage,$start){
        $query = $this->db
            ->join('menus',     'menus.id = permisos.menu_id', 'inner')
            ->join('groups',    'groups.id = permisos.group_id', 'inner');
        if($where = $this->input->post('buscar', TRUE))
            $query = $this->db->where('id =', $where);
        $query = $this->db->limit($perpage,$start)
            ->get('permisos');
        return $query->num_rows();
    }
    function getByLink($link){
        $query = $this->db
            ->select('id')
            ->where('link',$link)
            ->get('menus');
        return $query->row();
    }
    //buscarPermiso
    function lookupPermission($url, $perfil){
        $query = $this->db
            ->select('p.read,p.menu_id')
            ->join('menus m', 'm.id = p.menu_id')
            ->where('p.group_id', $perfil)
            ->like('m.link', $url)
            ->get('permisos p');
        return $query->row();
    }
    //buscarGrupo
    function lookUpGroup($usuario){
        $query = $this->db
            ->select('group_id')
            ->where('user_id', $usuario)
            ->get('users_groups');
        return $query->row();
    }
    function permissions($menu, $id_usuario) {
        $query = $this->db
            ->select('u.id, up.group_id, p.*')
            ->join('users_groups up', 'up.user_id = u.id')
            ->join('permisos p', 'p.group_id = up.group_id')
            ->where('u.id =', $id_usuario)
            ->where('p.menu_id =', $menu)
            ->limit('1')
            ->get('users u');
        return $query->row();
    }
}

我希望它能帮助你。

PS:1º)我不太喜欢getMenu函数,它不尊重MVC模式,而且查询也不安全(必须使用参数化方法?)。2º)此功能创建的html可以更改。3º)这里描述的大多数代码都不是我的。。。ion_auth库由Ben Edmunds制作,权限代码由一位不知名的"艺术家"制作。4º)如果你一丝不苟,许可证上有很多东西可以升级。。。