【问题标题】:MySQL password() function to PHPMySQL密码()函数到PHP
【发布时间】:2013-07-19 23:19:56
【问题描述】:

我尝试对此进行研究,但仍然没有答案。我朋友设计的一个程序使用 MySQL password() 函数写入 MySQL 数据库密码。

我正在寻找一种通过我设计的网络前端使用它的方法,但仍然没有运气。有人有什么建议吗?

密码看起来就像这个例子

mysql> SET old_passwords = 0;
mysql> SELECT PASSWORD('mypass');
+-------------------------------------------+
| PASSWORD('mypass')                        |
+-------------------------------------------+
| *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
+-------------------------------------------+

我只需要弄清楚如何把它变成一个函数,即 函数密码哈希

这是登录查询的其余示例

if (isset($_POST["username"], $_POST["password"], $_POST[$CONF["LOGIN_SIGNAL_TRIGGER"]])) {

    /*
        If we got a login signal, a password and a username, we will
        proceed to check login information. We will first extract
        the user row from the db.
    */
    $user = myF(myQ("
        SELECT `username`,`password`,`id`,`disable_until`,`active`
        FROM `[x]users` 
        WHERE LCASE(`username`)='".strtolower($_POST["username"])."'
    "));

    if (!$user["id"]) $GLOBALS["LOGIN_FAIL_TYPE"] = "e.user";
    elseif ($user["active"] != 1 && $CONF["LOGIN_REQUIRE_ACTIVE"]) $GLOBALS["LOGIN_FAIL_TYPE"] = "e.active";

    else {
        /*
            If the user's account 'disabled' value is greater than 
            the actual date value, and that the bruteforce protection
            system is enabled, we will show an error message
        */
        if (($user["disable_until"] > date("U")) && ($CONF["LOGIN_BRUTEFORCE_PROTECT:ENABLE"])) {
             $GLOBALS["LOGIN_FAIL_TYPE"] = "e.bruteforce";
            (isset($_SESSION["loginFailCount"])?session_unregister('loginFailCount'):false);
        }

        /*
            Account is not disabled
        */
        else {
            if ((isset($_SESSION["loginFailCount"])) && ($_SESSION["loginFailCount"] > $CONF["LOGIN_BRUTEFORCE_FAILCOUNT"])) {

                myQ("UPDATE `[x]users`
                    SET `disable_until` = ".(date("U")+$CONF["LOGIN_BRUTEFORCE_DISABLE_DURATION"])."
                    WHERE LCASE(`username`)='".strtolower($_POST["username"])."'
                    LIMIT 1"
                );

                (isset($_SESSION["loginFailCount"])?session_unregister('loginFailCount'):false);
                $GLOBALS["LOGIN_FAIL_TYPE"] = "e.bruteforce";
            }

            else {

                /*
                    All the information correct, we will proceed to login
                */
                if ($user["password"] == md5(trim($_POST["password"]))) {
                    $_SESSION["id"] = (integer)$user["id"];

                    session_write_close();

                    /*
                        Update the last login key
                    */
                    $me_last_login = me("last_login");
                    myQ("UPDATE `[x]users` SET `last_login`='".date("U")."' WHERE `id`='".me('id')."'");

                    /*
                        Route the user
                    */
                    if (!$GLOBALS["WAP_MODE"]) {
                        header("Location: ".(!$me_last_login?$CONF["LOGIN_FIRST_ROUTE_TO"]:$CONF["LOGIN_ROUTE_TO"]));
                    } else header("Location: {$CONF["WAP_LOGIN_ROUTE_TO"]}");

                } 

                else {
                    (isset($_SESSION["loginFailCount"])?$_SESSION["loginFailCount"]++:$_SESSION["loginFailCount"]=1);
                    $GLOBALS["LOGIN_FAIL_TYPE"] = "e.password";
                }
            }
        }
    }
}

if ((isset($_GET[$CONF["LOGOUT_SIGNAL_TRIGGER"]])) && (!isset($_POST[$CONF["LOGIN_SIGNAL_TRIGGER"]]))) {

    /*
        Handle admin swapping
    */
    if (isset($_SESSION["swap_id"])) {
        $_SESSION["id"] = $_SESSION["swap_id"];
        session_unregister("swap_id");
        header("Location: ?L=admin.index");
    }

    else {
        (isset($_SESSION["id"])?session_unregister('id'):false);
        (isset($_SESSION["SELF_USER_DATA"])?session_unregister('SELF_USER_DATA'):false);

        header("Location: {$CONF["LOGOUT_ROUTE_TO"]}");
    }
}

【问题讨论】:

  • 用它做什么?如果您只想使用 mysql 生成密码哈希 - 执行与问题中相同的查询
  • @user2603089 从您刚刚评论的代码来看,您似乎没有使用准备好的语句,这给您留下了一个巨大的安全漏洞。
  • 由@HieuNguyen 链接,它只是'*' . sha1(sha1('mypass', true)); - 但无论如何您都可以在数据库上调用该函数。也来自同一个问题The PASSWORD() function is used by the authentication system in MySQL Server; you should not use it in your own applications
  • @AD7six 是围绕md5 函数的串联让我这么认为。另外,我们不知道查询中还有什么。可能是"WHERE Password = ".mdf($_POST["adminpass"])." AND Username = ."$_POST["adminuser"];,它仍然对 SQL 注入开放。

标签: php mysql


【解决方案1】:

如果我对您的理解正确,则无需在 php 中复制 PASSWORD(),在您的选择中使用 PASSWORD() 一次性在 mysql 端进行所有验证

SELECT `username`,`password`,`id`,`disable_until`,`active` 
  FROM `[x]users`
 WHERE `username` = 'user1' 
   AND `password` = PASSWORD('password')

这里是SQLFiddle演示

除非您使用区分大小写的排序规则请勿在语句中的 username 列上使用 LCASE()。如果在该列上定义了任何索引,它会阻止 MySql 使用索引(索引)并导致对表进行全扫描。

附带说明:您的代码容易受到 sql 注入的攻击。考虑使用准备好的语句。

【讨论】:

  • 这有一个值得一提的安全隐患。特别是,可以记录 SQL 查询(例如 MySQL 的慢查询日志)。如果您在查询内部进行哈希处理,则密码可能会写入日志文件。 (还有其他安全隐患,但这是最紧迫的。)
  • 另一个问题是,如果 MySQL 再次更改 password 函数的定义,它将中断,就像他们在 MySQL 4.1 中所做的那样。
【解决方案2】:

首先使用mysql_escape_string($_POST['password']) 转义密码。

然后在

中将其用作 sql 查询中的变量
WHERE password_field = PASSWORD(USER_SUPPLIED_PASSWORD)

【讨论】:

    【解决方案3】:

    OP 询问如何在 php 中执行此操作。这是在 php 中的操作方法:

    function sqlPassword($input) {
        $pass = strtoupper(
                sha1(
                        sha1($input, true)
                )
        );
        $pass = '*' . $pass;
        return $pass;
    }
    

    为后代添加(没有理由使用它,如果 mysql 决定弃用 PASSWORD 函数,请使用它?,仅供参考)php 等效项的 mysql 等效项

    SELECT 
      UPPER(
        CONCAT('*', SHA1(UNHEX(SHA1('password'))))
      )
    

    另见MySQL Hashing Function Implementation

    【讨论】:

      【解决方案4】:

      从我编写的 Symfony2 应用程序的密码编码器界面复制粘贴,该应用程序使用 MySQL PASSWORD 函数从旧系统迁移:

      function mysqlPassword($raw) {
          return '*'.strtoupper(hash('sha1',pack('H*',hash('sha1', $raw))));
      }
      

      【讨论】:

        【解决方案5】:

        我的 MySQL 4.1 之前的代码版本。

        /* PHP implementation of MySQL pre-4.1 password function.
        Based on Perl Crypt::MySQL C code */
        function mysql_old_password_hash($password) {
          $len = strlen($password); 
          $add = 7;
          $nr  = 1345345333;
          $nr2 = 0x12345671;
          $tmp = 0;
        
          foreach (str_split($password) as $chr) {
             if ($chr == " " || $chr == "\t") {
               continue;
             }
        
             $tmp  = ord($chr);
             $nr  ^= ((($nr & 0x3f)+$add)*$tmp) + ($nr << 8);
             $nr2 += ($nr2 << 8) ^ $nr;
             $nr2 &= 0xffffffff; # We need to limit this to 32-bit
             $add += $tmp;
          }
        
          // Strip sign bit
          $nr &= 0x7fffffff;
          $nr2 &= 0x7fffffff;
          return sprintf("%08x%08x",$nr,$nr2);
        }
        

        对于 post-4.1、pre-5.7.something 版本,请查看 sjagr 或 chiliNUT 的答案。

        使用它的唯一原因是为了检查数据库中的密码,尽管 MySQL 建议不要将PASSWORD() 用于您自己的应用程序。

        【讨论】:

        • 嗯...有趣...最多可以使用 8 个字符...可能需要再次与 Crypt::MySQL 进行比较...。
        • 修复它,$nr2 扩展超过 32 位似乎是问题...
        • @zx1986: 我主要是把Crypt::MySQL Perl模块的XS(C)代码移植到了PHP上,好像主要是基于MySQL源代码... 代码不是特别简单跟随...(字符 ($tmp) 混入散列的第一部分 ($nr),第二部分 ($nr2) 以间接方式受到影响)。可能不是很好的 crypt....(Unix crypt 是一个不同的函数,它是基于 DES 的......)
        • @zx1986:32 位问题主要是检查 C 代码是否有不同(我知道第二部分 ($nr2) 会受到影响)。注意到它们的类型,尽管值得尝试用 and 限制值。那碰巧起作用了...(有一个小的测试shell函数,它输出通过Perl模块编码的相同文本,这个函数和MySQL本身......)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-23
        • 2018-11-15
        • 1970-01-01
        • 2017-03-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多