5

是否有任何工作包可以使用 PHP 更改 linux 用户密码?

我尝试使用 PECL:PAM,但尝试更改密码时出现错误。

编辑:

PHP代码:

echo pam_chpass($username, $password, $new_pass, &$error) ? 'good' : $error;

PHP(回声)输出:

Permission denied (in pam_authenticate)

从 /var/log/auth (这些实际上是以前的,由于某种原因,日志似乎无法正常工作 ATM):

Jun 11 15:30:20 veda php: pam_unix(php:chauthtok): conversation failed
Jun 11 15:30:20 veda php: pam_unix(php:chauthtok): password - (old) token not obtained
Jun 11 15:30:20 veda php: pam_winbind(php:chauthtok): valid_user: wbcGetpwnam gave WBC_ERR_DOMAIN_NOT_FOUND

其他:

抱歉之前缺乏细节,当我发布问题时我真的很累,但这仍然是一个蹩脚的借口。

4

5 回答 5

2

经过数小时的在线研究,我找不到一个超级好的选择,所以我实施了这个 hack。它使用本文使用 PHP 更改密码。

我还使用PECL:PAM 包来添加一点验证。

此页面位于安全的 HTTPS 文件夹中(通过 .htaccess 自动重定向)

<?php

$messages = array();

function change_password ($user, $currpwd, $newpwd) {

    // Open a handle to expect in write mode
    $p = popen('/usr/bin/expect','w');

    // Log conversation for verification
    $log = '/tmp/passwd_' . md5($user . time());
    $cmd .= "log_file -a \"$log\"; ";

    // Spawn a shell as $user
    $cmd .= "spawn /bin/su $user; ";
    $cmd .= "expect \"Password:\"; ";
    $cmd .= "send \"$currpwd\\r\"; ";
    $cmd .= "expect \"$user@\"; ";

    // Change the unix password
    $cmd .= "send \"/usr/bin/passwd\\r\"; ";
    $cmd .= "expect \"(current) UNIX password:\"; ";
    $cmd .= "send \"$currpwd\\r\"; ";
    $cmd .= "expect \"Enter new UNIX password:\"; ";
    $cmd .= "send \"$newpwd\\r\"; ";
    $cmd .= "expect \"Retype new UNIX password:\"; ";
    $cmd .= "send \"$newpwd\\r\"; ";
    $cmd .= "expect \"passwd: password updated successfully\"; ";

    // Commit the command to expect & close
    fwrite($p, $cmd); pclose ($p);

    // Read & delete the log
    $fp = fopen($log,r);
    $output = fread($fp, 2048);
    fclose($fp); unlink($log);
    $output = explode("\n",$output);

    return (trim($output[count($output)-2]) == 'passwd: password updated successfully') ? true : false;
}

function process_post() {

    if ((!isset($_SERVER['HTTP_REFERER'])) 
        || (strpos($_SERVER['HTTP_REFERER'], $_SERVER['SCRIPT_NAME']) === FALSE)) {

        echo "GO AWAY!";
        exit();
        return FALSE;

    }

    global $messages;

    $username           = trim($_POST['username']);
    $password_current   = trim($_POST['password_current']);
    $password_new       = trim($_POST['password_new']);
    $password_confirm   = trim($_POST['password_confirm']);

    // Check for blanks
    if ($username == '' || $password_current == '' || $password_new == '' || $password_confirm == '') {
        array_push(&$messages, "ERROR: You cannot leave any field empty.");
        return FALSE;
    }

    // Check username
    if (!ctype_alnum($username)) {
        array_push(&$messages, "ERROR: You've entered an invalid username.");
        return FALSE;
    }

    // Check to see if new password is correctly typed
    if ($password_new != $password_confirm) {       
        array_push(&$messages, "ERROR: New Password and Confirmation do not match.");
        return FALSE;
    }

    // Check if current password is valid (not really neccessary)
    if (!pam_auth($username, $password_current, &$error, FALSE)) {
        if (trim($error) == "Permission denied (in pam_authenticate)")
            array_push(&$messages, "ERROR: You've username/password was not accepted.");    
        else
            array_push(&$messages, "ERROR: " . $error);
        return FALSE;
    }

    if (change_password ($username, $password_current, $password_new))
        array_push(&$messages, "Password Successfully Changed");
    else 
        array_push(&$messages, "ERROR: Password change failed.");

}

if ($_SERVER['REQUEST_METHOD'] == 'POST') process_post();


?><html>
<head>


<title>Passwords</title>

<style type="text/css">

body {
    font-family: Verdana, Arial, sans-serif;
    font-size: 12px;
}

label {
    width: 150px;
    display: block;
    float: left;
}

input {
    float: left;
}

br {
    clear: both;
}

.message {
    font-size: 11px;
    font-weight: bold;
}

.error {
    color:#C00;
}


</style>

</head>


<body>

<h2>Change Passwords</h2>

<form action="<?= $_SERVER['SCRIPT_NAME'] ?>" method="post">

<fieldset>

<? if (count($messages) != 0) { 

    foreach ($messages as $message) { ?>

<p class="message<?= ((strpos($message, 'ERROR:') === FALSE) ? '' : ' error') ?>"><?= $message ?></p>

<? } } ?>

<label>Username: </label>
<input type="text" name="username" /><br />

<label>Current Password:</label>
<input type="password" name="password_current" /><br />

<label>New Password:</label>
<input type="password" name="password_new" /><br />

<label>Confirm Password:</label>
<input type="password" name="password_confirm" /><br />

<input type="reset" value="Reset" /> <input type="submit" value="Submit" />

</fieldset>


</form>


</body>
</html>

我也在https://serverfault.com/questions/150306/how-to-let-users-change-linux-password-from-web-browser/152409#152409中发布了这个问题/答案

于 2010-06-18T07:56:01.637 回答
2

除了 wag2369 发布的答案之外,请确保执行以下操作:

安装 pear,它是 PHP 的扩展管理器:

yum install pear

从 yum 安装 pam-devel

yum install pam-devel

安装 PHP PAM 扩展

pecl install --alldeps PAM

--alldeps:表示自动安装所有依赖项

修改文件/etc/php.ini并输入以下内容:

extension=pam.so
pam.servicename="php"

执行以下操作以允许 PAM php 服务:

cd /etc/pam.d
ln -s login /etc/pam.d/php

重启阿帕奇:

/etc/init.d/httpd restart

/etc/shadow 应该是可读的(这是一个安全漏洞,请重新考虑)

chmod g+r,o+r /etc/shadow

如果尚未安装,请安装 expect

yum install expect

修复 wag2369 贴出的代码中的 bug 或者直接复制下面的修改代码: 使用 array_push($error,..) 而不是 array_push(&$error, ...) 'passwd: password updated successfully' 不应该使用,使用“passwd:所有身份验证令牌更新成功。” 改为检查。

<?php
$messages = array();

function change_password ($user, $currpwd, $newpwd) {

    // Open a handle to expect in write mode
    $p = popen('/usr/bin/expect','w');

    // Log conversation for verification
    $log = '/tmp/passwd_' . md5($user . time());
    $cmd = "";
    $cmd .= "log_file -a \"$log\"; ";

    // Spawn a shell as $user
    $cmd .= "spawn /bin/su $user; ";
    $cmd .= "expect \"Password:\"; ";
    $cmd .= "send \"$currpwd\\r\"; ";
    $cmd .= "expect \"$user@\"; ";

    // Change the unix password
    $cmd .= "send \"/usr/bin/passwd\\r\"; ";
    $cmd .= "expect \"(current) UNIX password:\"; ";
    $cmd .= "send \"$currpwd\\r\"; ";
    $cmd .= "expect \"Enter new UNIX password:\"; ";
    $cmd .= "send \"$newpwd\\r\"; ";
    $cmd .= "expect \"Retype new UNIX password:\"; ";
    $cmd .= "send \"$newpwd\\r\"; ";
    $cmd .= "expect \"passwd: all authentication tokens updated successfully.\"; ";

    // Commit the command to expect & close
    fwrite($p, $cmd); pclose ($p);

    // Read & delete the log
    $fp = fopen($log,'r');
    $output = fread($fp, 2048);
    fclose($fp); unlink($log);
    $output = explode("\n",$output);

    return (trim($output[count($output)-2]) == 'passwd: all authentication tokens updated successfully.') ? true : false;
}

function process_post() {

    if ((!isset($_SERVER['HTTP_REFERER'])) 
        || (strpos($_SERVER['HTTP_REFERER'], $_SERVER['SCRIPT_NAME']) === FALSE)) {

        echo "GO AWAY!";
        exit();
        return FALSE;

    }

    global $messages;

    $username           = trim($_POST['username']);
    $password_current   = trim($_POST['password_current']);
    $password_new       = trim($_POST['password_new']);
    $password_confirm   = trim($_POST['password_confirm']);

    // Check for blanks
    if ($username == '' || $password_current == '' || $password_new == '' || $password_confirm == '') {
        array_push($messages, "ERROR: You cannot leave any field empty.");
        return FALSE;
    }

    // Check username
    if (!ctype_alnum($username)) {
        array_push($messages, "ERROR: You've entered an invalid username.");
        return FALSE;
    }

    // Check to see if new password is correctly typed
    if ($password_new != $password_confirm) {       
        array_push($messages, "ERROR: New Password and Confirmation do not match.");
        return FALSE;
    }

    // Check if current password is valid (not really neccessary)
    $error = '';
    if (!pam_auth($username, $password_current, $error, FALSE)) {
        if (trim($error) == "Permission denied (in pam_authenticate)")
            array_push($messages, "ERROR: Your username/password was not accepted.");    
        else
            array_push($messages, "ERROR: " . $error);
        return FALSE;
    }

    if (change_password ($username, $password_current, $password_new))
        array_push($messages, "Password Successfully Changed");
    else 
        array_push($messages, "ERROR: Password change failed.");

}

if ($_SERVER['REQUEST_METHOD'] == 'POST') process_post();


?><html>
<head>


<title>Passwords</title>

<style type="text/css">

body {
    font-family: Verdana, Arial, sans-serif;
    font-size: 12px;
}

label {
    width: 150px;
    display: block;
    float: left;
}

input {
    float: left;
}

br {
    clear: both;
}

.message {
    font-size: 11px;
    font-weight: bold;
}

.error {
    color:#C00;
}


</style>

</head>


<body>

<h2>Change Passwords</h2>

<form action="<?= $_SERVER['SCRIPT_NAME'] ?>" method="post">

<fieldset>

<? if (count($messages) != 0) { 

    foreach ($messages as $message) { ?>

<p class="message<?= ((strpos($message, 'ERROR:') === FALSE) ? '' : ' error') ?>"><?= $message ?></p>

<? } } ?>

<label>Username: </label>
<input type="text" name="username" value="halaluya" /><br />

<label>Current Password:</label>
<input type="password" name="password_current" value="dev0te@m" /><br />

<label>New Password:</label>
<input type="password" name="password_new" value="123" /><br />

<label>Confirm Password:</label>
<input type="password" name="password_confirm" value="123" /><br />

<input type="reset" value="Reset" /> <input type="submit" value="Submit" />

</fieldset>


</form>


</body>
</html>
于 2011-03-02T07:38:06.153 回答
1

直接从 PHP 更改 PAM 密码需要对系统文件和服务进行大量访问。这是因为 PAM 默认使用 pam_unix 模块,该模块将用户凭据存储在 root 拥有的系统文件中。克服这个问题的一个好方法是设置 PAM 以使用 pam_ldap 模块。这种方式 PAM 使用 LDAP 服务器对用户进行身份验证。然后从 PHP 中,您可以使用用户凭据绑定到 LDAP 服务器并更改他的密码。LDAP 授权机制可以处理此类修改的授权。(您的应用程序还应该强制执行授权规则,以提供分层安全性)

上面的配置并不简单。您必须首先设置 LDAP 服务器,然后将所有用户数据从系统文件(passwd、shadow)迁移到 LDAP 目录。(有自动化工具)。最后,您必须安装和设置 pam_ldap 模块。上述过程中的任何错误配置都可能导致严重的安全问题。

另请注意,这样您将通过您的应用程序将 LDAP 服务器公开给 Web。任何可能影响 LDAP 身份验证或授权机制的安全问题也会影响您的系统安全。

资源:

使用 LDAP 存储 POSIX 帐户:

http://www.ibm.com/developerworks/linux/library/l-openldap/

设置 PAM 以使用 LDAP 进行身份验证:

http://wiki.debian.org/LDAP/PAM

于 2012-05-19T17:24:01.473 回答
0

是否有任何工作包可以使用 PHP 更改 linux 用户密码?

这真的,真的很危险。假设您了解风险,那么您将意识到在应用更改之前需要构建许多约束,这些约束必须在允许更改密码的权限级别中实现 - 即运行它的代码必须是独立的可执行文件setuid executoin 或通过 sudo 从您的 php 代码中调用。

当然,没有理由不能用 PHP 编写独立代码,除了(至少,我上次看到这个)PHP 中的 PAM 绑定相当不成熟,

您可能想查看 chpasswd 程序(在 Redhat 和其他一些发行版上可用)或使用 proc_open('/usr/bin/passwd'... 并正确阅读并响应提示。

高温高压

C。

于 2010-06-13T22:42:21.460 回答
0

您可以使用 RSBAC 密码。

$ret = system("echo \"newpass newpass\" | rsbac_password -n");

if ($ret)
    echo "fail.";
else
    echo "done!";

容易多了。

于 2010-07-19T12:55:00.513 回答