如何验证域凭据?

我想要验证一组凭据对网域控制器。例如:

Username: STACKOVERFLOW\joel
Password: splotchy

方法1. 使用模拟查询 ActiveDirectory

很多人建议在 ActiveDirectory 中查询一些东西。如果抛出异常,那么您就知道凭据是无效的——正如 这个堆栈溢出的问题中所建议的那样。

然而,还有一些重要的 这种方法的缺点:

  1. 您不仅要对域帐户进行身份验证,还要执行隐式授权检查。也就是说,您正在使用模拟标记从 AD 读取属性。如果原本有效的帐户没有读取广告的权限怎么办?默认情况下,所有用户都具有读访问权限,但是可以设置域策略来禁用受限帐户(和组)的访问权限。

  2. 针对 AD 的绑定有严重的开销,必须在客户端加载 AD 模式缓存(DirectoryServices 使用的 ADSI 提供程序中的 ADSI 缓存)。这既是网络,也是广告服务器,资源消耗-和太昂贵的一个简单的操作,如认证用户帐户。

  3. 对于非异常情况,您依赖于异常故障,并假设这意味着无效的用户名和密码。其他问题(如网络故障、 AD 连接故障、内存分配错误等)则被误解为身份验证失败。

方法2. LogonUserWin32API

其他人则建议使用 LogonUser() API 函数。这听起来不错,但不幸的是,调用用户有时需要一个通常只给予操作系统本身的权限:

调用 LogonUser 的进程需要 SE _ TCB _ NAME 特权 调用进程没有这个 权限,LogonUser 失败,并且 返回 GetLastError ERROR _ PRIVILEGE _ NOT _ HELD.

在某些方面 例,调用 LogonUser 还必须具有 SE _ Change _ NOTIFY _ NAME 特权 启用; 否则,LogonUser 将失败 和 GetLastError 返回 错误 _ ACCESS _ DENIED。此特权是 本地系统不需要 会员帐户 管理员组的 默认情况下,SE _ ChangGE _ NOTIFY _ NAME 为 为所有用户启用,但一些 管理员可能会禁用它 所有人。

授予“ 作为操作系统的一部分”特权不是你想随意做的事情,正如微软在 知识库文章知识库文章中指出的那样:

这个过程正在召唤 LogonUser 必须具有 SE _ TCB _ NAME 特权(在用户管理器中,这是 作为操作的一部分 System ”right) . SE _ TCB _ NAME 特权是非常强大的 不应授予任何任意用户,以便他们可以 运行一个应用程序 ,该应用程序需要 验证证书。

此外,如果指定了空白密码,则对 LogonUser()的调用将失败。


对一组域凭据进行身份验证的正确方法是什么?


发生了是从托管代码调用,但这是一个一般的 Windows 问题。可以假定客户具有。安装了 NET Framework 2.0。

129110 次浏览

C# in .NET 3.5 using System.DirectoryServices.AccountManagement.

 bool valid = false;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
valid = context.ValidateCredentials( username, password );
}

This will validate against the current domain. Check out the parameterized PrincipalContext constructor for other options.

using System;
using System.Collections.Generic;
using System.Text;
using System.DirectoryServices.AccountManagement;


class WindowsCred
{
private const string SPLIT_1 = "\\";


public static bool ValidateW(string UserName, string Password)
{
bool valid = false;
string Domain = "";


if (UserName.IndexOf("\\") != -1)
{
string[] arrT = UserName.Split(SPLIT_1[0]);
Domain = arrT[0];
UserName = arrT[1];
}


if (Domain.Length == 0)
{
Domain = System.Environment.MachineName;
}


using (PrincipalContext context = new PrincipalContext(ContextType.Domain, Domain))
{
valid = context.ValidateCredentials(UserName, Password);
}


return valid;
}
}

Kashif Mushtaq Ottawa, Canada

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.DirectoryServices.AccountManagement;


public struct Credentials
{
public string Username;
public string Password;
}


public class Domain_Authentication
{
public Credentials Credentials;
public string Domain;


public Domain_Authentication(string Username, string Password, string SDomain)
{
Credentials.Username = Username;
Credentials.Password = Password;
Domain = SDomain;
}


public bool IsValid()
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain))
{
// validate the credentials
return pc.ValidateCredentials(Credentials.Username, Credentials.Password);
}
}
}

I`m using the following code to validate credentials. The method shown below will confirm if the credentials are correct and if not wether the password is expired or needs change.

I`ve been looking for something like this for ages... So i hope this helps someone!

using System;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
using System.Runtime.InteropServices;


namespace User
{
public static class UserValidation
{
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(string principal, string authority, string password, LogonTypes logonType, LogonProviders logonProvider, out IntPtr token);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
enum LogonProviders : uint
{
Default = 0, // default for platform (use this!)
WinNT35,     // sends smoke signals to authority
WinNT40,     // uses NTLM
WinNT50      // negotiates Kerb or NTLM
}
enum LogonTypes : uint
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkCleartext = 8,
NewCredentials = 9
}
public  const int ERROR_PASSWORD_MUST_CHANGE = 1907;
public  const int ERROR_LOGON_FAILURE = 1326;
public  const int ERROR_ACCOUNT_RESTRICTION = 1327;
public  const int ERROR_ACCOUNT_DISABLED = 1331;
public  const int ERROR_INVALID_LOGON_HOURS = 1328;
public  const int ERROR_NO_LOGON_SERVERS = 1311;
public  const int ERROR_INVALID_WORKSTATION = 1329;
public  const int ERROR_ACCOUNT_LOCKED_OUT = 1909;      //It gives this error if the account is locked, REGARDLESS OF WHETHER VALID CREDENTIALS WERE PROVIDED!!!
public  const int ERROR_ACCOUNT_EXPIRED = 1793;
public  const int ERROR_PASSWORD_EXPIRED = 1330;


public static int CheckUserLogon(string username, string password, string domain_fqdn)
{
int errorCode = 0;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain_fqdn, "ADMIN_USER", "PASSWORD"))
{
if (!pc.ValidateCredentials(username, password))
{
IntPtr token = new IntPtr();
try
{
if (!LogonUser(username, domain_fqdn, password, LogonTypes.Network, LogonProviders.Default, out token))
{
errorCode = Marshal.GetLastWin32Error();
}
}
catch (Exception)
{
throw;
}
finally
{
CloseHandle(token);
}
}
}
return errorCode;
}
}

Here's how to determine a local user:

    public bool IsLocalUser()
{
return windowsIdentity.AuthenticationType == "NTLM";
}

Edit by Ian Boyd

You should not use NTLM anymore at all. It is so old, and so bad, that Microsoft's Application Verifier (which is used to catch common programming mistakes) will throw a warning if it detects you using NTLM.

Here's a chapter from the Application Verifier documentation about why they have a test if someone is mistakenly using NTLM:

Why the NTLM Plug-in is Needed

NTLM is an outdated authentication protocol with flaws that potentially compromise the security of applications and the operating system. The most important shortcoming is the lack of server authentication, which could allow an attacker to trick users into connecting to a spoofed server. As a corollary of missing server authentication, applications using NTLM can also be vulnerable to a type of attack known as a “reflection” attack. This latter allows an attacker to hijack a user’s authentication conversation to a legitimate server and use it to authenticate the attacker to the user’s computer. NTLM’s vulnerabilities and ways of exploiting them are the target of increasing research activity in the security community.

Although Kerberos has been available for many years many applications are still written to use NTLM only. This needlessly reduces the security of applications. Kerberos cannot however replace NTLM in all scenarios – principally those where a client needs to authenticate to systems that are not joined to a domain (a home network perhaps being the most common of these). The Negotiate security package allows a backwards-compatible compromise that uses Kerberos whenever possible and only reverts to NTLM when there is no other option. Switching code to use Negotiate instead of NTLM will significantly increase the security for our customers while introducing few or no application compatibilities. Negotiate by itself is not a silver bullet – there are cases where an attacker can force downgrade to NTLM but these are significantly more difficult to exploit. However, one immediate improvement is that applications written to use Negotiate correctly are automatically immune to NTLM reflection attacks.

By way of a final word of caution against use of NTLM: in future versions of Windows it will be possible to disable the use of NTLM at the operating system. If applications have a hard dependency on NTLM they will simply fail to authenticate when NTLM is disabled.

How the Plug-in Works

The Verifier plug detects the following errors:

  • The NTLM package is directly specified in the call to AcquireCredentialsHandle (or higher level wrapper API).

  • The target name in the call to InitializeSecurityContext is NULL.

  • The target name in the call to InitializeSecurityContext is not a properly-formed SPN, UPN or NetBIOS-style domain name.

The latter two cases will force Negotiate to fall back to NTLM either directly (the first case) or indirectly (the domain controller will return a “principal not found” error in the second case causing Negotiate to fall back).

The plug-in also logs warnings when it detects downgrades to NTLM; for example, when an SPN is not found by the Domain Controller. These are only logged as warnings since they are often legitimate cases – for example, when authenticating to a system that is not domain-joined.

NTLM Stops

5000 – Application Has Explicitly Selected NTLM Package

Severity – Error

The application or subsystem explicitly selects NTLM instead of Negotiate in the call to AcquireCredentialsHandle. Even though it may be possible for the client and server to authenticate using Kerberos this is prevented by the explicit selection of NTLM.

How to Fix this Error

The fix for this error is to select the Negotiate package in place of NTLM. How this is done will depend on the particular Network subsystem being used by the client or server. Some examples are given below. You should consult the documentation on the particular library or API set that you are using.

APIs(parameter) Used by Application    Incorrect Value  Correct Value
=====================================  ===============  ========================
AcquireCredentialsHandle (pszPackage)  “NTLM”           NEGOSSP_NAME “Negotiate”