MySQL的password()函数转换为PHP

10

我尝试研究了一下,但仍然没有找到答案。我朋友设计的一个程序使用MySQL密码()函数向MySQL数据库写入密码。

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

这些密码看起来就像这个例子。

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

我只需要想出如何将这个转化为一个函数,例如:function password_hash。

下面是用于登录查询的其余部分,作为示例。

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使用什么类型的哈希? - Barmar
1
用于什么?如果您只想使用mysql生成密码哈希-执行与问题中相同的查询。 - AD7six
1
@user2603089,从你刚才评论的代码来看,似乎你没有使用预处理语句,这会为你留下一个巨大的安全漏洞。 - christopher
1
正如@HieuNguyen所链接的那样,它只是'*' . sha1(sha1('mypass', true)); - 但你仍然可以在数据库中调用该函数。同样来自相同问题的内容:PASSWORD()函数是MySQL服务器中身份验证系统使用的;你不应该在自己的应用程序中使用它。 - AD7six
1
@AD7six,正是围绕md5函数的连接使我这样想。此外,我们不知道查询中还有什么内容。它可能是"WHERE Password = ".mdf($_POST["adminpass"])." AND Username = ."$_POST["adminuser"];,这仍然极易受到SQL注入攻击的影响。 - christopher
显示剩余6条评论
5个回答

10

OP 问如何在 PHP 中实现此操作。以下是在 PHP 中实现此操作的方法:

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

为了纪念 (如果mysql决定废弃PASSWORD函数,您没有理由使用它,请仅用于信息目的),这是mysql等效于php等效的函数。

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

另请参见MySQL哈希函数的实现


5
如果我正确理解了你的意思,那么在PHP中不需要再次使用PASSWORD()函数来验证密码。你可以在MySQL端一次性进行所有验证,使用PASSWORD()函数在SELECT语句中实现,如下所示:
SELECT `username`,`password`,`id`,`disable_until`,`active` 
  FROM `[x]users`
 WHERE `username` = 'user1' 
   AND `password` = PASSWORD('password')

这里是 SQLFiddle 演示。

如果您没有使用区分大小写的排序规则,请勿在语句中使用 LCASE() 处理 username 列。如果该列上定义了索引,这会防止 MySql 使用索引(indices),并导致对整个表进行全面扫描。

顺便提一下:您的代码容易受到 SQL 注入攻击。建议使用预处理语句。


8
这里有一个需要提及的安全问题。特别是,SQL查询可能会被记录下来(例如MySQL的慢查询日志)。如果你在查询内部进行哈希处理,密码可能会被写入日志文件中。(还有其他安全影响,但这是最紧急的。) - Corbin
另一个问题是,如果MySQL再次更改password函数的定义,就会出现错误。就像他们在MySQL 4.1中所做的那样。 - Gert van den Berg

4

下面是我为一个使用MySQL PASSWORD 函数的旧系统迁移到Symfony2应用程序编写的密码编码器界面:

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

1

我使用的是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);
}

对于4.1版本之后,5.7.x之前的版本,请参考sjagr或chiliNUT的回答。

使用这个的唯一原因是为了检查密码是否存在于一个数据库中,而该数据库是在MySQL建议不要在自己的应用程序中使用PASSWORD()的情况下编写的。


1
嗯...有趣...它可以工作到8个字符...也许需要再次与Crypt::MySQL进行比较.... - Gert van den Berg
1
已解决,似乎是 $nr2 超出 32 位的范围导致了问题... - Gert van den Berg
1
@zx1986:我主要是将Crypt :: MySQL Perl模块的XS(C)代码移植到PHP上,这似乎主要基于MySQL源代码... 代码不是特别容易理解...(字符($tmp)混入哈希的第一部分($nr),第二部分($nr2)以间接方式受到影响)。可能不是很好的加密....(Unix crypt是一个不同的函数,它是基于DES的...) - Gert van den Berg
1
@zx1986:32位问题主要是检查C代码中可能不同的部分(我知道第二部分($nr2)是受影响的)。注意它们的类型,认为值限制可以尝试使用and。那恰好起作用了...(有一个小测试shell函数,输出相同的文本通过Perl模块编码,这个函数和MySQL本身....) - Gert van den Berg
1
@zx1986:http://cpansearch.perl.org/src/IKEBE/Crypt-MySQL-0.04/lib/Crypt/MySQL.xs - Gert van den Berg
显示剩余5条评论

0

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

然后将其作为变量插入到SQL查询中。

WHERE password_field = PASSWORD(USER_SUPPLIED_PASSWORD)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接