Security 安全性-查看和编辑id在地址栏中可见

Security 安全性-查看和编辑id在地址栏中可见,security,cakephp,cakephp-3.0,Security,Cakephp,Cakephp 3.0,CakePHP版本3.5.5 该id在地址栏中可见,可供查看和编辑,这会给我的应用程序带来安全风险。同一公司的任何登录用户都可以更改地址栏中的id,并查看或编辑详细信息 不允许他们访问的用户数 IE:可在地址栏中手动更改为并输入。这将显示不允许的用户1215的详细信息 为了克服这个问题,我选择了允许用户编辑的id,并使用以下代码检查url中的id是否为这些id之一: public function view($id = null) { if ($this->request->

CakePHP版本3.5.5

该id在地址栏中可见,可供查看和编辑,这会给我的应用程序带来安全风险。同一公司的任何登录用户都可以更改地址栏中的id,并查看或编辑详细信息 不允许他们访问的用户数

IE:可在地址栏中手动更改为并输入。这将显示不允许的用户1215的详细信息

为了克服这个问题,我选择了允许用户编辑的id,并使用以下代码检查url中的id是否为这些id之一:

public function view($id = null)
{
    if ($this->request->is('get')) {

        // Select the permitted ids.
       if (superuser) { // example to explain only
            $query = $this->Users->find()
                ->where(['companyid' => $cid])
                ->andWhere(['status' => 1])
                ->toArray();
       }
       elseif (manager) { // example to explain only
           $query = $this->Users->find()
                ->where(['areaid' => $areaid])
                ->andWhere(['status' => 1])
                ->toArray();
       }
       elseif (team leader) { // example to explain only
           $query = $this->Users->find()
                ->where(['teamid' => $teamid])
                ->andWhere(['status' => 1])
                ->toArray();
       }

        // Check if the edit id is in the array of permitted ids.
        $ids = array_column($query, 'id');
        $foundKey = array_search($id, $ids);

        // If the edit id is not in the array of permitted ids redirect to blank.
        if (empty($foundKey)) {
            // Handle error.
        }

        $user = $this->Users->get($id);       
        $this->set('user', $user);
        $this->set('_serialize', ['user']);
    }
    else {
        // Handle error.
    }
}
我的问题:上面的代码是实现这一点的最佳方法还是有更好的方法

这段代码确实有效,但因为它与安全性有关,我希望任何能改进它或指出它的弱点的输入

/////////////////////////////////////////////////////////////////////////////
根据cgTag的要求,请参见下文

我的应用程序有超级用户、经理、团队领导和用户

经理管理一个可以包含多个团队的区域

团队领导者领导一个团队,并且必须属于一个区域

用户被分配到一个区域或团队

例如:

地区是英国
英格兰队
苏格兰队
威尔士队

地区是美国
球队位于佛罗里达州
球队来自加利福尼亚
团队位于德克萨斯州

在索引上-超级用户可以看到公司中的所有超级用户、经理、团队领导和用户

在索引上,经理可以看到自己和所在区域的用户、所在区域的团队领导以及团队中的用户

在索引上-团队领导可以看到自己和团队中的用户

我的问题是,如果英国地区的经理在其中一条记录上单击“编辑”,则该记录显示的url为

然后,假设这个不满的经理猜了一下,将url更改为并提交,然后显示此记录。(此记录可以是任何人、超级用户、其他经理、不在其所在区域的团队领导或不在其所在区域的用户

然后,经理可以更改电子邮件地址并提交此邮件,我需要防止这种情况

我的解决方案是重申我在view and edit类中对索引所做的超级用户、经理和团队领导的查找。这确保了经理只能查看或编辑其所在区域的人员

希望我已经解释得足够好了,但如果不让我知道,我会再次尝试

谢谢

/////////////////////////////////////////////////////////////////////////////

多亏了cgTag,我对这种方法更有信心,但我不能使用这段代码,因为您正确地假设我使用的是一个id来选择所有公司的结果,但我使用的是一个40个字符的字符串。我这样做是为了使我的sql查询更加健壮

除非您拥有所需的所有信息,否则您不可能帮助我,因此我在下面发布了一份准确的陈述:

public function view($id = null)
{
    if(!$this->request->is('get') || !$id) {
        //throw new ForbiddenException();
        echo 'in request is NOT get or id NOT set ' . '<hr />';
    }

    $user_id = $this->Auth->user('id');

    // regular users can never view other users.
    if($user_id !== $id) {
        //throw new ForbiddenException();
        echo 'in $user_id !== $id ' . '<hr />';
    }   

    // Declare client id 1.  
    if ($this->cid1() === false) {
        echo 'in throw exception ' . '<hr />';  
    }
    else {
        $c1 = null;
        $c1 = $this->cid1();
    }     

    $company_ids = $this->getCompanyIds($c1);
    $area_ids = $this->getAreaIds($user_id, $c1);
    $team_ids = $this->getTeamIds($user_id, $c1);  

    // company_id does not exist which will cause an unknown column error.
    // The column I select by is cid_1 so I have changed this column to cid_1 as shown below. 
    $user = $this->Users->find()
    ->where([
        'id' => $id,
        'cid_1 IN' => $company_ids,
        'area_id IN' => $area_ids,
        'team_id IN' => $team_ids,
        'status' => 1
    ])
    ->firstOrFail();

    $this->set(compact('user'));
}
使用此代码,我得到以下错误:

错误:SQLSTATE[21000]:基数冲突:1241个操作数应包含1列

我不知道你的例子是否适用于这些新信息,但至少你现在掌握了所有信息

如果可以的话,那就太好了,但如果不能的话,我真的不介意。我非常感谢你抽出时间来帮助我

谢谢你

/////////////////////////////////////////////////////////////////////////////

@tarikul05-感谢您的输入

您的建议与我解决此安全问题的第一次尝试非常相似,但我通过模糊性实现了安全性,并将id隐藏在一个80字符的字符串中,如下例所示

// In a cell
public function display($id = null)
{
    // Encrypt the id to pass with view and edit links.
    $idArray = str_split($id);
    foreach($idArray as $arrkey => $arrVal) {           
        $id0 = "$idArray[0]";
        $id1 = "$idArray[1]";
        $id2 = "$idArray[2]";
        $id3 = "$idArray[3]";
    } 

    // Generate string for the id to be obscured in.
    $enc1 = null;
    $enc1 = sha1(uniqid(mt_rand(), true));
    $enc2 = null;
    $enc2 = sha1(uniqid(mt_rand(), true));
    $encIdStr = $enc1 . $enc2;

    // Split the string.
    $encIdArray = null;
    $encIdArray = str_split($encIdStr); 

    // Generate the coded sequence.
    $codedSequence = null;
    $codedSequence = array(9 => "$id0", 23 => "$id1", 54 => "$id2", 76 => "$id3");

    // Replace the id in the random string.
    $idTemp = null;    
    $idTemp = array_replace($encIdArray, $codedSequence);   

    // Implode the array.
    $encryptedId = null;
    $encryptedId = implode("",$idTemp);

    // Send the encrypted id to the view.
    $this->set('encryptedId', $encryptedId);
}
然后用

// In function in the app controller
public function decryptTheId($encryptedId = null)
{
    $idArray = str_split($encryptedId);
    foreach($idArray as $arrkey => $arrVal) {          
        $id0 = "$idArray[9]";
        $id1 = "$idArray[23]";
        $id2 = "$idArray[54]";
        $id3 = "$idArray[76]";
    }
    $id = null;
    $id = $id0.$id1.$id2.$id3;       
    return $id;
}
问题是,在测试时,我设法使脚本出错,从而暴露了数组位置,这可能会通过模糊性原则破坏安全性,并使黑客更容易操作

你的建议比我的模糊方法更简洁,但我相信md5已经被破解,因此不应该使用

我不是安全专家,但在我看来,根据允许的id数组检查视图和编辑id是解决这个问题的最安全的方法

也许我错了,但如果我这样做的话,无论黑客在地址栏中尝试了什么,他们都无法看到或编辑他们不想看到的数据,这会使url更干净

我最初寻找/希望的是一个解决这个问题的蛋糕方法/函数,但我在食谱中找不到任何东西


无论如何谢谢。Z.

我会简化您的代码,以便获取用户记录的SQL仅在当前用户具有权限的情况下才能找到该记录。当您依赖于这些条件的关联数据时。即使必须使用联接,也要遵循此方法

创建SQL条件,然后对查询调用
firstOrFail()
。如果记录不匹配,将抛出
NotFoundException

public function view($id = null) {
     if(!$this->request->is('get') || !$id) {
           throw new ForbiddenException();
     }

     $user_id = $this->Auth->user('id');

     // regular users can never view other users.
     if($user_id !== $id) {
            throw new ForbiddenException();
     }

     $company_ids = $this->getCompanyIds($user_id);
     $area_ids = $this->getAreaIds($user_id);
     $team_ids = $this->getTeamIds($user_id);

     $user = $this->Users->find()
        ->where([
           'id' => $id
           'company_id IN' => $company_ids,
           'area_id IN' => $area_ids,
           'team_id IN' => $team_ids,
           'status' => 1
        ])
        ->firstOrFail();

     $this->set(compact('user'));
}
当一个用户属于数据的层次结构时,上面的逻辑应该是合理的。在这里,他们可以查看许多用户,但只有当这些用户属于他们也可以访问的上层关联之一时

它之所以有效,是因为where条件的子句中有

注意:如果数组为空,IN操作符将抛出一个错误。当您有可以看到所有“团队”的用户时,只需排除where条件,而不是使用空数组

这里的关键是让函数返回一个允许的父关联数组,例如;
getCompanyIds($user\u id)
只返回当前用户允许访问的公司id

我认为如果您以这种方式实现它,那么逻辑就很容易理解,安全性是可靠的,并且是一个简单的
firstOrFail()
public function view($id = null) {
     if(!$this->request->is('get') || !$id) {
           throw new ForbiddenException();
     }

     $user_id = $this->Auth->user('id');

     // regular users can never view other users.
     if($user_id !== $id) {
            throw new ForbiddenException();
     }

     $company_ids = $this->getCompanyIds($user_id);
     $area_ids = $this->getAreaIds($user_id);
     $team_ids = $this->getTeamIds($user_id);

     $user = $this->Users->find()
        ->where([
           'id' => $id
           'company_id IN' => $company_ids,
           'area_id IN' => $area_ids,
           'team_id IN' => $team_ids,
           'status' => 1
        ])
        ->firstOrFail();

     $this->set(compact('user'));
}