安全性 - 地址栏

时间:2018-01-06 13:38:10

标签: security cakephp cakephp-3.0

CakePHP版本3.5.5

ID在地址栏中可见,用于查看和编辑,这对我的应用程序造成了安全风险。同一公司的任何登录用户都可以更改地址栏中的ID并查看或编辑详细信息 他们不被允许的用户。

IE:https://localhost/crm/users/edit/1378可以在地址栏中手动更改为https://localhost/crm/users/edit/1215并输入。这将显示不允许的用户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的要求,请参阅下文。

我的应用有超级用户,经理,团队负责人和用户。

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

团队领导带领一个团队,必须属于一个区域。

将用户分配到某个区域或团队。

例如:

区域是英国
球队是英格兰队 团队是苏格兰
团队是威尔士

区域是美国
团队是佛罗里达州 团队是加利福尼亚州 团队是德克萨斯州

关于索引 - 超级用户可以看到公司中的所有超级用户,经理,团队负责人和用户。

关于索引 - 经理会看到他们自己和他们所在地区的用户,他们所在地区的团队领导以及团队中的用户。

关于索引 - 团队领导者在团队中看到自己和用户

我的问题是,英国地区的经理点击其中一条记录上的编辑,该记录显示的网址为https://localhost/crm/users/edit/1378

然后说这个心怀不满的经理做出猜测并将网址更改为https://localhost/crm/users/edit/1215并提交,然后显示此记录。 (此记录可以是任何人,超级用户,另一位经理,不在其所在地区的团队负责人或不在其所在地区的用户。

然后,这位经理可以更改说出电子邮件地址并提交此信息,这是我需要防范的类型情况。

我的解决方法是重复查看我在视图和编辑类中对索引所做的超级用户,经理和团队负责人。这可以确保管理员只能查看或编辑他们所在区域的某个人。

希望我已经解释得很好,但如果不是让我知道,我还会再去。

感谢。 Z.

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

谢谢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'));
}

功能:

public function cid1()
{
    $session = $this->request->session();
    if ($session->check('Cid.one')) {
        $c1 = null;
        $c1 = $session->read('Cid.one');
        if (!is_string($c1) || is_numeric($c1) || (strlen($c1) !== 40)) {
            return false;
        }
        return $c1;
    }
    return false;
}

public function getCompanyIds($c1 = null)
{        
    $query = $this->Users->find()
        ->where(['status' => 1])
        ->andWhere(['cid_1' => $c1]);
    return $query;
}

public function getAreaIds($c1 = null, $user_id = null)
{
    $query = $this->Users->find()
        ->where(['status' => 1])
        ->andWhere(['cid_1' => $c1])
        ->andWhere(['area_id' => $user_id]);
    return $query;
}

public function getTeamIds($c1 = null, $user_id = null)
{
    $query = $this->Users->find()
        ->where(['status' => 1])
        ->andWhere(['cid_1' => $c1])
        ->andWhere(['team_id' => $user_id]);
    return $query;
}

使用此代码,我收到以下错误:

错误:SQLSTATE [21000]:基数违规:1241操作数应包含1列

我不知道您的示例是否可以使用这些新信息,但至少您现在拥有所有信息。

如果它可以被推荐很好,但如果没有,我真的不介意。我非常感谢您放下一边试图提供帮助的时间。

谢谢Z

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

@ 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 cleaner。

我最初看到/希望的是一个Cake方法/功能,它解决了这个问题,但我在菜谱中找不到任何东西。

非常感谢。 ž。

1 个答案:

答案 0 :(得分:1)

我会简化您的代码,以便获取用户记录的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子句。

  

注意:如果数组为空,则IN运算符会引发错误。如果您的用户可以看到所有&#34;团队&#34;只是排除where where而不是使用空数组。

这里的关键是让函数返回一组允许的父关联,例如; getCompanyIds($user_id)只会返回当前用户也可以访问的公司ID。

我认为如果你以这种方式实现它,那么逻辑很容易理解,安全性很强,简单的firstOrFail()会阻止访问。