Windows上使用本地用户帐户的基于PHP表单的身份验证

Windows上使用本地用户帐户的基于PHP表单的身份验证

PHP Forms-Based Authentication on Windows using Local User Accounts

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

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


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

它还说

1
2
3
LogonUser Win32 API does not require TCB privilege in Microsoft Windows Server 2003, however, for downlevel compatibility, this is still the best approach.

<p>On Windows XP, it is no longer required that a process have the SE_TCB_NAME privilege in order to call LogonUser. Therefore, the simplest method to validate a user's credentials on Windows XP, is to call the LogonUser API.</p>

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

编辑:
我还尝试利用System.Security.Principal.WindowsIdentity和php的com / .net扩展名。但是dotnet构造函数似乎不允许将参数传递给对象构造函数,并且我的" experiment "从GetType()获取程序集(以及与之创建实例()一起使用)已失败,并出现了" unknown zval "错误。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 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源代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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>

好问题!

我已经考虑了一下……我想不出一个好的解决方案。我能想到的是一个可能可行的可怕骇客技巧。在看到将近一天没有人发布有关该问题的答案之后,我觉得很糟糕,但是可以解决问题。

在系统运行时,SAM文件超出范围。有一些DLL注入技巧可以使您工作,但最终您只会得到密码哈希,并且您必须对用户提供的密码进行哈希处理,以始终与它们匹配。

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

  • 在服务器上创建一个文件共享,并对其进行分配,以便仅授予您希望能够以其身份登录的帐户访问权限。
  • 在PHP中,使用system命令调用wsh脚本:使用网站用户提供的用户名和密码安装共享。记录驱动器是否正常运行,然后卸载驱动器。
  • 以某种方式收集结果。结果可以在脚本的stdout上返回给php,或者希望使用脚本的返回码返回。
  • 我知道它不漂亮,但是应该可以。

    我觉得很脏:|

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


    推荐阅读