【问题标题】:is isset($_SESSION['admin']) a dangerous way to grant access?isset($_SESSION['admin']) 是一种危险的授予访问权限的方法吗?
【发布时间】:2012-05-13 06:49:26
【问题描述】:

如果(为了论证)在 php 中授予了“admin-access”:

if (isset($_SESSION['admin']))  // this session would be set
{ // grant access; }            // after a successful login
else { //redirect ;}

如果您知道会话的名称是什么(在本例中是管理员),这会是一件特别容易绕过和伪造的事情吗?

换句话说,如果脚本所要求的只是要“设置”的会话,那么有人可以轻松伪造 $_SESSION 吗?

【问题讨论】:

  • 如果你只使用上面的代码,那么是的,这很危险。会话可以被修复、劫持……你需要考虑到这一点。

标签: php session login


【解决方案1】:

使用isset() 对安全性来说还不错。这取决于你如何使用它的逻辑。如果您不仅要检查isset(),而且还要检查它的值,那就太好了。

例如:

if( isset($_SESSION['admin']) && $_SESSION['admin'] == true ) { 
  // grant access
} else { 
  //redirect 
}

或者是这样的:

if( isset($_SESSION['admin']) && $_SESSION['admin'] == '1' ) { 
  // grant access
} else { 
  //redirect 
}

【讨论】:

  • 是的,非常正确。虽然,$_SESSION 变量不能在其他任何地方神奇地设置。除非您以这种方式编码,否则用户无法以任何方式影响您的 $_SESSION 变量。所以理论上isset就足够了。
  • @raPHPid:是的,您是对的,但这取决于您在 $_SESSION['admin'] 中存储的内容。可能是 OP 在 $_SESSION['admin'] 中存储 adminsuperadmin
  • Javascript Injection 可用于处理编码不佳的身份验证。虽然我不确定 Session 变量是否会受此影响,但如果是这种情况,isset() 绝对是不够的。
  • 啊,非常正确。考虑它并考虑应用程序的未来,检查变量的值可能是非常明智的。好点子。虽然,我想到了,那个编码员可能也希望超级管理员访问这些管理功能。因此,需要另一种解决方案来分隔管理员,可能是级别或其他。实际上,知道人们使用什么会很有趣,我通常使用 USERLEVEL 类型的解决方案(如果从头开始)。我必须搜索stackoverflow...
  • @maiorano84:没有办法向 $_SESSION 注入变量(除非您将其编码为 <%php $_SESSION[$_GET['varname']=$_GET['varvalue']; 之类的代码。还有许多其他攻击方式(XSS、SQL)或劫持会话,其中与注入变量无关。如果(注入变量)是可能的,那么所有世界的 PHP 应用程序都将处于危险之中。
【解决方案2】:

我更喜欢更安全的方式,比如我在旧应用程序中使用的这个类:

class auth {

    protected $userID;
    protected $password;
    protected $username;
    protected $remember;
    protected $userType;

    public function checkAuth($username,$password,$remember=0) {
        global $db;

        $this->password = sha1($password);
        $this->username = strtolower($username);
        $this->remember = $remember;

        $sth = $db->prepare("SELECT `id`,`username`,`password`,`type` FROM `user` WHERE `username` = :username AND `active` = '1' LIMIT 1");
        $sth->execute(array(
            ':username' => $this->username
        ));
        $result = $sth->fetchAll();
        $this->userType = $result[0]['type'];

        if (@$result[0]['password'] == $this->password) {
            $this->userID = $result[0]['id'];
            $this->makeLogin();
            return true;
        } else {
            return false;
            exit;
        }
    }

    private function makeLogin() {
        $securityInformation = $this->username . '|-|' . $this->password . '|-|' . $this->userID . '|-|' . $this->userType;
        $hash = $this->encode($securityInformation);
        if ($this->remember) {
            setcookie('qdata',$hash,time()+604800,'/');
        } else {
            $_SESSION['qdata'] = $hash;
        }
        $this->updateStats();
    }

    public function isLogin() {
        global $db, $ua, $cache;

        $data = $this->getUserInfo();
        if ($data) {

            $sth = $db->prepare('SELECT `password`,`last_login_ip` FROM `user` WHERE `id` = :ID LIMIT 1');
            $sth->execute(array(
                ':ID' => $data['userID']
            ));

            $result = $sth->fetchAll();
            if ( ($result[0]['password'] == $data['password']) AND ($result[0]['last_login_ip'] == $ua->getIP()) ) {
                return true;
            } else {
                return false;
            }

        }
    }

    public function logout() {
        if (@isset($_COOKIE['qdata'])) {
            setcookie('qdata','',time()-200, '/');
        } 
        if (@isset($_SESSION['qdata'])) {
            unset($_SESSION['qdata']);
        }
    }

    private function parseHash($hash) {
        $userData = array();
        list($userData['username'],$userData['password'],$userData['userID'],$userData['userType']) = explode('|-|',$this->decode($hash));
        return $userData;
    }

    public function getUserInfo() {
        if (@isset($_COOKIE['qdata'])) {
            $data = $this->parseHash($_COOKIE['qdata']);
            return $data; 
        } elseif (@isset($_SESSION['qdata'])) {
            $data = $this->parseHash($_SESSION['qdata']);
            return $data;
        } else {
            return false;
        }
    }

    private function encode($str) {
        $chr = '';
        $prt = '';
        for($i=0;$i < strlen($str);$i++) {
            $prt = (chr(ord(substr($str,$i,1)) + 3)) . chr(ord(substr($str,$i,1)) + 2);
            $chr =  $prt . $chr;
        }
        return str_rot13($chr);
    }

    private function decode($str) {
        $chr = '';
        $prt = '';
        $str = str_rot13($str);
        for($i=0;$i < strlen($str);$i++) {
            if($i % 2 == 0) {
                $prt = (chr(ord(substr($str,$i,1)) - 3));
                $chr = $prt . $chr;
            }
        }
        return $chr;    
    }
}

如果你不喜欢这种方法,至少在管理表中存储一个特殊的键,并在值中使用带有该键的会话,每次加载页面时还要检查登录是否验证。

【讨论】:

  • 这需要数据库,而 OP 的初始解决方案不需要。
  • 抱歉,我确实使用数据库来最初检查 passowrd + 用户名中的匹配项,但这个问题更适合单独的页面,只需检查用户名是否已设置,以及是否安全足够的。这是一个很好的答案,有很多东西可以学习,谢谢!我很想看看其他意见是什么
  • @Tadeck 是的,你是对的,但我想告诉他一些关于验证用户身份的更好方法的想法
  • @PayamB:请问您为什么将用户密码存储到 cookie 中?这不是降低安全性吗?用户认证一次还不够,当用户持有认证cookie时,他是“有效的”并通过了认证?
  • @raPHPid sha1 hashed password 由 encode() 函数编码将被存储在 cookie 上,想想你在你的工作场所登录到你的帐户后,cookie 会记住你您在家中更改了密码,没有检查密码您上次在工作中登录仍然有效,即使您更改了密码。
猜你喜欢
  • 2016-03-23
  • 1970-01-01
  • 2018-12-20
  • 1970-01-01
  • 1970-01-01
  • 2022-10-15
  • 2021-11-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多