返回

PHP 生成 Dovecot SHA512-CRYPT 密码哈希 (doveadm)

php

PHP 中使用 Doveadm 生成 SHA512-CRYPT 密码哈希

咱们来聊聊怎么在 PHP 注册页面里,利用 doveadm 工具,给用户的密码生成 Dovecot 邮箱服务所使用的 SHA512-CRYPT 哈希。

啥问题?

你现在用的是 hash('sha256', $pass) 来给密码加密,但你的邮件服务器需要 doveadm 生成的 SHA512-CRYPT 格式。直接用 $password=(/usr/bin/doveadm pw -s SHA512-CRYPT -p "$pass") 这种方式在 PHP 里好像不太灵光。 那么问题来了,咋整?

咋回事儿?

hash('sha256', $pass) 这玩意儿生成的是标准的 SHA256 哈希,跟 Dovecot 想要的格式对不上号。邮件服务器认的是 doveadm 生成的特定格式的密码哈希。直接把 doveadm 命令写在 PHP 里头,就像 $password=(/usr/bin/doveadm ...),PHP 不会把它当成一个外部命令去执行,所以搞不定。

解决办法!安排!

咱有几个招儿可以解决这个问题。

1. 使用 shell_exec()

shell_exec() 函数可以直接在 PHP 里执行 shell 命令,并把结果返回来。

原理:

shell_exec() 就是让 PHP “变身” 成一个 shell 终端,执行你给它的命令,然后把终端的输出结果抓回来,原样交给你。

代码示例:

<?php
$pass = 'MySuperSecretPassword'; // 用户的密码,从表单里获取
$command = "/usr/bin/doveadm pw -s SHA512-CRYPT -p " . escapeshellarg($pass);
$passwordHash = shell_exec($command);

if ($passwordHash === null) {
    // 命令执行失败了!处理错误
    echo "密码哈希生成失败!";
} else {
    // $passwordHash 现在就是 Dovecot 能认的哈希了
     echo "生成的密码哈希: " . $passwordHash;
    //  这里把 $passwordHash 存到数据库里
}

?>

安全提示:

  • escapeshellarg() : 务必使用 escapeshellarg() 来处理用户输入的密码。这哥们儿能把密码里头的特殊字符给转义掉,避免有人搞破坏(比如命令注入)。
  • 错误处理: 检查 shell_exec() 的返回值。要是返回 null,说明命令执行出岔子了。

2. 使用 exec()

exec() 函数跟 shell_exec() 类似,也是执行外部命令。但它能给你更多控制权。

原理:

exec() 执行命令,但它会把输出结果放到一个数组里(一行一个元素),还会告诉你命令的退出状态码(0 表示成功,非 0 表示失败)。

代码示例:

<?php
$pass = 'AnotherSuperSecretPassword';
$command = "/usr/bin/doveadm pw -s SHA512-CRYPT -p " . escapeshellarg($pass);
$output = [];
$return_var = 0;
exec($command, $output, $return_var);

if ($return_var !== 0) {
    // 出错了!
     echo "命令执行失败,状态码: " . $return_var;
     //  看看 $output 里有啥错误信息
} else {
    //  成功了!$output[0] 里头应该就是密码哈希
      echo "生成的密码哈希: " . $output[0];
     // 把 $output[0] 存数据库
}
?>

安全提示:

shell_exec() 一样,用 escapeshellarg(),检查 $return_var

3. 使用 proc_open() (进阶玩法)

如果你需要对命令的执行过程进行更精细的控制,比如想单独处理标准输出和标准错误,那就得请出 proc_open() 大神了。

原理:

proc_open() 不仅仅是执行命令,它还会给你创建几个“管道”。你可以通过这些管道跟命令进行双向通信,分别读取它的标准输出、标准错误,甚至还可以给它发送输入。

代码示例:

<?php
$pass = 'YetAnotherSecretPassword';
$command = "/usr/bin/doveadm pw -s SHA512-CRYPT -p " . escapeshellarg($pass);

$descriptorspec = array(
   0 => array("pipe", "r"),  // 标准输入,我们用不着
   1 => array("pipe", "w"),  // 标准输出,存到这里
   2 => array("pipe", "w")   // 标准错误,也存到这里
);

$process = proc_open($command, $descriptorspec, $pipes);

if (is_resource($process)) {
    // $pipes[0]  不用管
    // $pipes[1]  标准输出
    // $pipes[2]  标准错误
    $stdout = stream_get_contents($pipes[1]);
    fclose($pipes[1]);

    $stderr = stream_get_contents($pipes[2]);
    fclose($pipes[2]);

    $return_value = proc_close($process);

    if ($return_value !== 0) {
      echo "出大事儿了!错误信息: " . $stderr;
    } else {
         echo "搞定!密码哈希: " . $stdout;
        // 把 $stdout 存数据库
    }
} else
{
    echo '启动进程失败了.';
}
?>

安全提示:

  • 还是那句话,escapeshellarg() 不能少。
  • 确保正确关闭了所有管道 (fclose()) 和进程 (proc_close())。
  • 标准错误管道一定要检查,通常都把重要的错误消息输出在那里.

4. 使用 PHP 的 Doveadm 扩展 (如果有的话)

有些时候,社区已经为一些特定的服务和工具提供了PHP的专用扩展,如果能找到并使用 Doveadm 的 PHP 扩展 (如果存在),这是最理想、最高效也最安全的方式。

原理:
这些扩展库已经封装底层的C/C++库来实现底层功能。通常提供与原生PHP函数几乎一致的接口.

如何做

  1. 检查是否存在 : 在PHP官方的扩展库或者PECL中搜索doveadmdovecot.
  2. 安装扩展 : 如果存在扩展, 通常可以使用pecl install或者包管理器(如aptyum)进行安装.
  3. 配置php.ini : 在php.ini文件中启用扩展.
  4. 使用扩展提供的函数 : 使用文档中说明的特定函数完成哈希生成. 避免了系统调用带来的安全风险.

虽然我当前没找到官方或者被广泛采用的doveadm专用PHP扩展. 但是建议还是先检查和搜索一遍.

总结一下

上面这几招,都能帮你用 PHP 生成 Dovecot 兼容的 SHA512-CRYPT 密码哈希。选哪个,看你的具体需求。如果只是简单用用,shell_exec() 或者 exec() 就够了。如果想要更精细的控制,就上 proc_open()。当然最佳方式是看下有没有官方扩展直接可用. 记住,安全第一,escapeshellarg() 和错误处理不能忘!