【问题标题】:How do I validate Active Directory creds over LDAP + SSL?如何通过 LDAP + SSL 验证 Active Directory 凭据?
【发布时间】:2018-03-16 06:24:54
【问题描述】:

我正在尝试使用 .NET 3.5 System.DirectoryServices.AccountManagement 命名空间通过我们的 Active Directory LDAP 服务器验证用户凭据通过 SSL 加密的 LDAP 连接。这是示例代码:

using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:389", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate))
{
    return pc.ValidateCredentials(_username, _password);
}

此代码在不安全的 LDAP(端口 389)上运行良好,但我不希望以明文形式传输用户/密码组合。但是当我更改为 LDAP + SSL(端口 636)时,出现以下异常:

System.DirectoryServices.Protocols.DirectoryOperationException: The server cannot handle directory requests.
  at System.DirectoryServices.Protocols.ErrorChecking.CheckAndSetLdapError(Int32 error)
  at System.DirectoryServices.Protocols.LdapSessionOptions.FastConcurrentBind()
  at System.DirectoryServices.AccountManagement.CredentialValidator.BindLdap(NetworkCredential creds, ContextOptions contextOptions)
  at System.DirectoryServices.AccountManagement.CredentialValidator.Validate(String userName, String password)
  at System.DirectoryServices.AccountManagement.PrincipalContext.ValidateCredentials(String userName, String password)
  at (my code)

端口 636 用于其他活动,例如查找该 LDAP/AD 条目的非密码信息...

UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, _username)

...所以我知道这不是我的 LDAP 服务器的 SSL 设置,因为它可以通过 SSL 进行其他查找。

有没有人接到ValidateCredentials(...) 的电话以通过 SSL 工作?你能解释一下怎么做吗?还是有另一种/更好的方法来安全地验证 AD/LDAP 凭据?

【问题讨论】:

  • 这是一篇 MSDN 文章,用于对基于 SSL 的 LDAP 进行故障排除:support.microsoft.com/kb/938703
  • 感谢您的链接。但是对于我执行的所有其他 LDAP 查询,我可以再次通过 LDAPS(端口 636)进行通信。 ValidateCredentials() 似乎有些不寻常。不过,我会更详细地阅读这篇文章。
  • 密码应该以明文传输 - 不是散列 - 通过安全连接到支持密码质量检查和密码历史强制执行的服务器,除非 LDAP 客户端提供密码质量和历史检查,否则服务器将无法强制执行质量和历史记录。

标签: c# .net active-directory ldap directoryservices


【解决方案1】:

感谢一位同事,我能够使用 System.DirectoryServices.Protocols 命名空间验证凭据。代码如下:

// See http://support.microsoft.com/kb/218185 for full list of LDAP error codes
const int ldapErrorInvalidCredentials = 0x31;

const string server = "sd.example.com:636";
const string domain = "sd.example.com";

try
{
    using (var ldapConnection = new LdapConnection(server))
    {
        var networkCredential = new NetworkCredential(_username, _password, domain);
        ldapConnection.SessionOptions.SecureSocketLayer = true;
        ldapConnection.AuthType = AuthType.Negotiate;
        ldapConnection.Bind(networkCredential);
    }

    // If the bind succeeds, the credentials are valid
    return true;
}
catch (LdapException ldapException)
{
    // Invalid credentials throw an exception with a specific error code
    if (ldapException.ErrorCode.Equals(ldapErrorInvalidCredentials))
    {
        return false;
    }

    throw;
}

我不喜欢使用 try/catch 块来控制决策逻辑,但它确实有效。 :/

【讨论】:

  • 您似乎试图与 AD LDS(即轻量级目录服务)和 AD DS(常规 Active Directory)通信。如果是这种情况,那么您的原始代码不起作用,因为您指定了错误的 ContextType。根据 Microsoft 文档,如果您想查询 AD LDS,则需要指定 ContextType.ApplicationDirectoryContextType.Domain 仅适用于常规 Active Directory。 docs.microsoft.com/en-us/dotnet/api/…
【解决方案2】:

也许这是另一种方式。验证凭据没有什么异常。 ContextOptions 必须正确设置。

默认值:

ContextOptions.Negotiate | ContextOptions.Signing | ContextOptions.Sealing

添加 SSL:

ContextOptions.Negotiate | ContextOptions.Signing | ContextOptions.Sealing | ContextOptions.SecureSocketLayer

ContextOptions.Negotiate 或 ContextOptions.SimpleBind 是必需的。或者您的服务器需要执行身份验证的任何内容。 ContextOptions 只支持 OR 位对位。

您也可以尝试在 ValidateCredentials 方法中直接设置 ContextOptions。

using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:636", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate | ContextOptions.SecureSocketLayer))
{
    return pc.ValidateCredentials(_username, _password);
}

或者

using (var pc = new PrincipalContext(ContextType.Domain, "sd.example.com:636", "DC=sd,DC=example,DC=com", ContextOptions.Negotiate))
{
    return pc.ValidateCredentials(_username, _password, ContextOptions.Negotiate | ContextOptions.SecureSocketLayer);
}

【讨论】:

  • 我看到你说“或者你的服务器需要执行身份验证。”。我如何知道服务器需要什么来进行身份验证?我正在使用以前设置了一些选项的服务器,但我不知道它们现在是什么。我可以以某种方式检查它们吗?
【解决方案3】:

对我来说,ValidateCredentials 方法工作得很好。我发现问题出在托管 AD 的服务器上(我正在使用 AD LDS)。您需要将服务器证书与 AD 实例相关联。因此,如果您的实例名为“MyAD”​​(或 ActiveDirectoryWebService),您需要打开 MMC,进入“证书”模块,选择“服务帐户”,然后从列表中选择“MyAD”​​。从那里您可以将 SSL 证书添加到“MyAD”​​个人存储中。这最终启动了 SSL 处理。

根据我对 LdapConnection 方法的了解以及您省略回调函数的事实,我怀疑您没有验证服务器证书。这是一项繁琐的工作,ValidateCredentials 是免费的。可能没什么大不了,但仍然存在安全漏洞。

【讨论】:

    【解决方案4】:

    我知道这已经过时了,但是对于再次遇到这种情况的人来说:

    PrincipalContext.ValidateCredentials(...) 默认情况下会尝试打开 SSL 连接 (ldap_init(NULL, 636)),然后设置选项 LDAP_OPT_FAST_CONCURRENT_BIND

    如果存在(受信任?)客户端证书,则 LDAP 连接是隐式绑定的,并且无法再启用快速绑定。 PrincipalContext 不考虑这种情况,并以意外的DirectoryOperationException 失败。

    解决方法:要尽可能支持 SSL,但有备用方案,请首先使用默认选项(即没有选项)调用 ValidateCredentials(...)。如果DirectoryOperationException 失败,请通过指定ContextOptions(协商|密封|签名)重试,这就是ValidateCredentials 在内部为预期的LdapException 所做的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-15
      • 2011-10-30
      • 1970-01-01
      • 2013-05-10
      • 1970-01-01
      • 1970-01-01
      • 2020-02-16
      • 1970-01-01
      相关资源
      最近更新 更多