5

我正在运行 PHP、Apache 和 Windows。我没有域设置,所以我希望我的网站的基于表单的身份验证使用 Windows 内置的本地用户帐户数据库(我认为它称为 SAM)。

我知道如果设置了 Active Directory,您可以使用 PHP LDAP 模块在脚本中进行连接和身份验证,但没有 AD 就没有 LDAP。独立机器的等价物是什么?

4

3 回答 3

3

我也没有找到一个简单的解决方案。有使用 CreateObject 和 WinNT ADSI 提供程序的示例。但最终他们都遇到 了 Active Directory 服务接口 WinNT 提供程序的用户身份验证问题。我不是 100% 确定,但我WSH/网络连接方法也有同样的问题。
根据如何在 Microsoft 操作系统上验证用户凭据,您应该使用 LogonUser 或 SSPI。
它还说

在 Microsoft Windows Server 2003 中,LogonUser Win32 API 不需要 TCB 权限,但是,为了向下兼容,这仍然是最好的方法。

在 Windows XP 上,不再需要进程具有 SE_TCB_NAME 权限才能调用 LogonUser。因此,在 Windows XP 上验证用户凭据的最简单方法是调用 LogonUser API。

因此,如果我确定不需要 Win9x/2000 支持,我会编写一个将 LogonUser 暴露给 php 的扩展。
您可能还对 NT Accounts 的用户身份验证感兴趣。它使用 w32api 扩展,需要一个支持 dll ...我宁愿写那个小的 LogonUser-extension ;-)
如果这不可行,我可能会研究 IIS 的 fastcgi 模块以及它的稳定性并让IIS 处理身份验证。

编辑:
我也尝试过利用System.Security.Principal.WindowsIdentity和 php 的com/.net 扩展名。但是 dotnet 构造函数似乎不允许将参数传递给对象构造函数,并且我从 GetType() 获取程序集(以及 CreateInstance())的“实验”因“未知 zval”错误而失败。

于 2008-09-14T09:22:01.387 回答
0

好问题!

我已经考虑过了……我想不出一个好的解决方案。我能想到的是一个可怕的骇人听闻的骇客,它可能会奏效。在看到将近一天没有人发布这个问题的答案后,我认为这是一个不好的答案,但可以工作的答案是可以的。

SAM 文件在系统运行时不受限制。有一些 DLL 注入技巧,您可能可以使用它们,但最终您只会得到密码哈希,并且无论如何您都必须对用户提供的密码进行哈希处理以匹配它们。

您真正想要的是尝试根据 SAM 文件对用户进行身份验证的东西。我认为您可以通过执行以下操作来做到这一点。

  1. 在服务器上创建文件共享并进行设置,以便只有您希望能够登录的帐户才能获得访问权限。
  2. 在 PHP 中,使用系统命令调用 wsh 脚本,该脚本: 使用网站用户提供的用户名和密码安装共享。记录它是否有效,如果有效则卸载驱动器。
  3. 以某种方式收集结果。结果可以在脚本的标准输出上返回给 php,或者希望使用脚本的返回码。

我知道它不漂亮,但它应该工作。

我觉得脏:|

编辑:调用外部 wsh 脚本的原因是 PHP 不允许您使用 UNC 路径(据我所知)。

于 2008-09-14T02:13:33.803 回答
0

构建 PHP 扩展需要在硬盘空间方面进行相当大的投资。由于我已经安装了 GCC (MinGW) 环境,因此我决定从 PHP 脚本启动进程来降低性能。源代码如下。

// Usage: logonuser.exe /user username /password password [/domain domain]
// Exit code is 0 on logon success and 1 on failure.

#include <windows.h>

int main(int argc, char *argv[]) {
    HANDLE r = 0;
    char *user = 0;
    char *password = 0;
    char *domain = 0;
    int i;

    for(i = 1; i < argc; i++) {
        if(!strcmp(argv[i], "/user")) {
            if(i + 1 < argc) {
                user = argv[i + 1];
                i++;
            }
        } else if(!strcmp(argv[i], "/domain")) {
            if(i + 1 < argc) {
                domain = argv[i + 1];
                i++;
            }
        } else if(!strcmp(argv[i], "/password")) {
            if(i + 1 < argc) {
                password = argv[i + 1];
                i++;
            }
        }
    }

    if(user && password) {
        LogonUser(user, domain, password, LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, &r);
    }
    return r ? 0 : 1;
}

接下来是演示其用法的 PHP 源代码。

if($_SERVER['REQUEST_METHOD'] == 'POST') {
    if(isset($_REQUEST['user'], $_REQUEST['password'], $_REQUEST['domain'])) {
        $failure = 1;
        $user = $_REQUEST['user'];
        $password = $_REQUEST['password'];
        $domain = $_REQUEST['domain'];

        if($user && $password) {
            $cmd = "logonuser.exe /user " . escapeshellarg($user) . " /password " . escapeshellarg($password);
            if($domain) $cmd .= " /domain " . escapeshellarg($domain);
            system($cmd, $failure);
        }

        if($failure) {
            echo("Incorrect credentials.");
        } else {
            echo("Correct credentials!");
        }
    }
}
?>
<form action="<?php echo(htmlentities($_SERVER['PHP_SELF'])); ?>" method="post">
    Username: <input type="text" name="user" value="<?php echo(htmlentities($user)); ?>" /><br />
    Password: <input type="password" name="password" value="" /><br />
    Domain: <input type="text" name="domain" value="<?php echo(htmlentities($domain)); ?>" /><br />
    <input type="submit" value="logon" />
</form>
于 2008-09-15T20:07:18.130 回答