这是一种安全的方法,oop

时间:2012-05-11 19:26:22

标签: php

    $salt = $this->get_salt($username);

    if (is_null($salt)) {
        return FALSE;
    }

    $password = sha1(SITE_KEY . $password . $salt);
    $sth = $this->db->prepare("SELECT id, username, active FROM user WHERE username = ? AND password = ?");
    $sth->setFetchMode(PDO::FETCH_OBJ);
    $sth->execute(array($username, $password));

    if (($result = $sth->fetch()) !== FALSE) {
        return $result;
    }

    return FALSE;

这让我感到担忧:

  

我没有误解登录方法。我只是觉得不应该   返回那个对象。我可能错了,你在做什么是完美的   很好,但我对此表示怀疑。您正在从中返回完整的用户对象   数据库,密码和所有,可能不安全的脚本。有人   可能会创建一个新文件然后做类似的事情   后续代码var_dump($ userObject);并拥有所有这些信息

     

另外,我发现从中返回“魔法”属性并不直观   一些东西。这只是另一个未经验证的事情   在使用之前。如果你要把它移到一个单独的方法中   你的auth类并让它返回你可以运行的“active”值   如果没有您的login.php脚本,您需要进行任何验证   更聪明。

     

:再看一遍,虽然你已经需要知道了   登录信息,如果您以这种方式滥用该对象。它是   在我看来,更好的是,如果有某种形式将它分开   泄漏不是说我知道有人能做到这一点。只是   把它想象成一个潜在的循环漏洞。

     

我不会假装了解恶意用户会做些什么。   我只是知道让他们更容易这样做似乎并不明智。   也许它没有任何问题,我不是安全专家。   我只是在盯着那些东西,如果我要写它,我会改变   因为它们对我来说似乎很危险如果你真的想知道它是不是   危险与否,我建议将这个例子提交给常规   stackoverflow社区,看看他们说了什么。

他有什么看法吗?该方法将在我拥有的页面控制器中使用:

$user = $auth->login('username, password)

if ($user) {
//do some additional checks... set session variables
}

2 个答案:

答案 0 :(得分:8)

不,您提供的代码不安全。

首先,您使用的是非迭代的简单哈希。有关原因的信息,请参阅this answerthis blog post

其次,为什么要分别储存盐? salt 必须对每条记录都是唯一的,以便它能够正常工作。所以把它放在一边的逻辑地方就是密码。在这种情况下,您要进行两次查询而不是一次(假设盐不是从用户名派生的,而且确实是随机的)。如果它不是随机的,它应该是。有关详细信息,请参阅this post

第三,您接触到针对密码哈希的计时攻击,因为数据库未使用计时免疫比较。有关详细信息,请参阅this articlethis article

不要自己动手。 确实很难安全地完成。使用图书馆。您可以使用PHPASSPHP-PasswordLib ...

编辑:收到你朋友的评论:

  

我没有误解登录方法。我只是不认为它应该返回该对象。我可能错了,你在做什么都很好,但我对此表示怀疑。您将完整的用户对象从数据库,密码和所有内容返回到可能不安全的脚本。有人可能会创建一个新文件然后执行类似var_dump($ userObject)的操作;并拥有所有这些信息

这是一个有效的观点。但此时,如果用户可以注入var_dump($userObject),他可以运行$this->db->prepare("SELECT * FROM user");并获取全部您的用户数据,而不仅仅是他的。因此,虽然有必要指出,但尝试保护是不值得的。除非你得到真正的肛门并将其全部放入数据库中的存储过程并从表中进行有限的读取访问(因此您无法从用户表中进行选择)。但是在那时,您需要从头开始重新设计整个应用程序,因为除了通过SP之外,您永远无法获得任何用户信息。这很难做得很好(复杂性是安全的敌人)。

最后,我认为你首先要远远更好地防范脚本注入。特别是如果你没有专业的管理员可以保护你的服务器到防止那种攻击所需的荒谬数量(PHP进程中的chroot jails等)。

  

另外,我发现从某些东西中返回“魔法”属性并不直观。这只是在使用之前未经验证的另一件事。如果您要将其移至auth类中的单独方法并让其返回“active”值,则可以运行您需要的任何验证,而无需使用login.php脚本。

那里有什么神奇的属性?你是直接使用这些物品吗?或者您是否使用它们来填充用户对象(然后知道如何处理数据)...?只是在那个snippit,我没有看到你的朋友指向的问题......

  

:再次查看它,如果您以这种方式滥用该对象,则需要知道登录信息。在我看来,如果存在某种泄漏,将其分开仍然会更好。不是说我知道有人能做到这一点。把它想象成一个不太可能的循环漏洞。

实际上,由于上​​面提到的时间攻击,你只需要知道一个有效的用户名......

  

我不会假装了解恶意用户会做些什么。我只是知道让他们更容易这样做似乎并不明智。也许它没有任何问题,我不是安全专家。我只是在讨论一些事情,如果我要写它,我会改变,因为它们对我来说似乎有害。如果您真的想知道它是否有危险,我建议将该示例提交给常规的stackoverflow社区,看看他们说了什么。

总的来说,这是一个很好的建议。请记住,复杂性是安全的敌人。因此,如果使其变得更加困难会增加很多复杂性,那么添加新漏洞的可能性就会大大增加(例如引入的时间攻击)。我在简单方面犯了错误。我有经过充分测试的库来处理复杂的部分(比如密码散列),但我写的代码很简单。

考虑最强的加密算法:One Time Pad。该算法很简单:

encrypted = plaintext XOR key

但没有钥匙就无法解密。因此,算法的安全性实际上在于秘密,而不在算法中。这是你想要的。该算法应该是公开的,安全性不应该依赖于它的保密性。否则它会通过默默无闻变得安全。在你破裂之前,这给你一种温暖的模糊感觉......

答案 1 :(得分:1)

有关如何使用 bcrypt 哈希密码,请参阅 this 答案。它比你的更安全,因为它允许故意减慢散列算法(所以如果有人试图暴力,它们将被限制在每秒1M次迭代,而不是1B,这是小时和年之间的差异)。

此外,(与安全性无关),您应该使用PDO的命名占位符,而不是未命名的占位符(:placeholder而不是?)。

此外,你可能想要构建一个完整的User对象并返回它,它将更容易使用(假设你有一个,你应该!)。