ドメインコントローラーに対して一連の資格情報を検証したい。例えば。:
_Username: STACKOVERFLOW\joel
Password: splotchy
_
多くの人が、Active Directoryに何かを照会することを提案しています。例外がスローされた場合、資格情報が無効であることがわかります- this stackoverflow question で提案されているように。
いくつかの深刻な このアプローチの欠点 があります:
ドメインアカウントを認証するだけでなく、暗黙的な承認チェックも行っています。つまり、偽装トークンを使用してADからプロパティを読み取ります。そうでなければ有効なアカウントにADから読み取る権利がない場合はどうなりますか?既定では、すべてのユーザーに読み取りアクセス権がありますが、制限されたアカウント(またはグループ)のアクセス許可を無効にするようにドメインポリシーを設定できます。
ADに対するバインドには重大なオーバーヘッドがあり、ADスキーマキャッシュをクライアントで読み込む必要があります(DirectoryServicesが使用するADSIプロバイダーのADSIキャッシュ)。これはネットワークとADサーバーの両方であり、リソースを消費します。また、ユーザーアカウントの認証などの単純な操作には高すぎます。
非例外的なケースでは例外の失敗に依存しており、それは無効なユーザー名とパスワードを意味すると想定しています。他の問題(たとえば、ネットワーク障害、AD接続障害、メモリ割り当てエラーなど)は、認証障害として誤って解釈されます。
その他 は LogonUser()
API関数の使用を提案しています。これはいいように思えますが、残念ながら、呼び出し側のユーザーは、通常、オペレーティングシステム自体にのみ許可を与える必要があります。
LogonUserを呼び出すプロセスには、SE_TCB_NAME特権が必要です。呼び出しプロセスにこの特権がない場合、LogonUserは失敗し、GetLastErrorはERROR_PRIVILEGE_NOT_HELDを返します。
場合によっては、LogonUserを呼び出すプロセスのSE_CHANGE_NOTIFY_NAME特権も有効にする必要があります。そうでない場合、LogonUserは失敗し、GetLastErrorはERROR_ACCESS_DENIEDを返します。この権限は、ローカルシステムアカウントまたは管理者グループのメンバーであるアカウントには必要ありません。デフォルトでは、SE_CHANGE_NOTIFY_NAMEはすべてのユーザーに対して有効になっていますが、一部の管理者はすべてのユーザーに対して無効にする場合があります。
「オペレーティングシステムの一部として機能する」特権を与えることは、あなたがやりたいことではありません-Microsoftが 知識ベース記事 で指摘しているように=:
... LogonUserを呼び出すプロセスには、SE_TCB_NAME特権が必要です(ユーザーマネージャーでは、これは「オペレーティングシステムの一部として機能する」権限です)。 SE_TCB_NAME特権は非常に強力であり、資格情報を検証する必要があるアプリケーションを実行できるように、任意のユーザーにを付与するべきではありません。
さらに、空白のパスワードが指定されている場合、LogonUser()
への呼び出しは失敗します。
ドメイン資格情報のセットを認証する適切な方法は何ですか?
Ihappenはマネージコードから呼び出しますが、これは一般的なWindowsの質問です。顧客に.NET Framework 2.0がインストールされていると想定できます。
System.DirectoryServices.AccountManagement を使用した.NET 3.5のC#。
bool valid = false;
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
valid = context.ValidateCredentials( username, password );
}
これにより、現在のドメインに対して検証されます。他のオプションについては、パラメーター化されたPrincipalContextコンストラクターを確認してください。
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);
}
}
}
次のコードを使用して資格情報を検証しています。以下に示す方法は、資格情報が正しいかどうか、パスワードが期限切れか変更が必要かどうかを確認します。
私は長年にわたってこのようなものを探していました...だから私はこれが誰かを助けることを願っています!
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;
}
}
ローカルユーザーを決定する方法は次のとおりです。
public bool IsLocalUser()
{
return windowsIdentity.AuthenticationType == "NTLM";
}
もうNTLMを使用しないでください。非常に古く、ひどいため、MicrosoftのApplication Verifier(一般的なプログラミングのミスを検出するために使用されます)は、NTLMの使用を検出すると警告をスローします。
誰かが誤ってNTLMを使用しているかどうかをテストする理由に関するApplication Verifierのドキュメントの章を次に示します。
NTLMプラグインが必要な理由
NTLMは、アプリケーションやオペレーティングシステムのセキュリティを侵害する可能性のある欠陥を持つ古い認証プロトコルです。最も重要な欠点は、サーバー認証がないことです。これにより、攻撃者はユーザーをだまして、なりすましのサーバーに接続させることができます。サーバー認証の欠落の結果として、NTLMを使用するアプリケーションは、「リフレクション」攻撃として知られるタイプの攻撃に対しても脆弱になる可能性があります。この後者により、攻撃者はユーザーの認証会話を正当なサーバーにハイジャックし、それを使用してユーザーのコンピューターに対して攻撃者を認証できます。 NTLMの脆弱性とそれらを悪用する方法は、セキュリティコミュニティでの研究活動の増加の標的です。
Kerberosは長年使用されてきましたが、多くのアプリケーションはNTLMのみを使用するように作成されています。これにより、アプリケーションのセキュリティが不必要に低下します。ただし、KerberosはすべてのシナリオでNTLMを置き換えることはできません。主に、クライアントがドメインに参加していないシステム(ホームネットワークがおそらく最も一般的です)に対して認証する必要がある場合です。 Negotiateセキュリティパッケージは、可能な限りKerberosを使用し、他のオプションがない場合にのみNTLMに戻る下位互換性のある妥協を可能にします。 NTLMの代わりにNegotiateを使用するようにコードを切り替えると、アプリケーションの互換性をほとんどまたはまったく導入せずに、お客様のセキュリティが大幅に向上します。ネゴシエート自体は特効薬ではありません。攻撃者がNTLMに強制的にダウングレードできる場合もありますが、これらを悪用するのは非常に困難です。ただし、すぐに改善される点の1つは、Negotiateを正しく使用するように作成されたアプリケーションがNTLMリフレクション攻撃の影響を自動的に受けないことです。
NTLMの使用に対する最後の注意事項として、Windowsの将来のバージョンでは、オペレーティングシステムでNTLMの使用を無効にすることが可能になります。アプリケーションがNTLMに強く依存している場合、NTLMが無効になっていると認証に失敗します。
プラグインの仕組み
Verifierプラグは次のエラーを検出します。
NTLMパッケージは、AcquireCredentialsHandle(またはより高いレベルのラッパーAPI)の呼び出しで直接指定されます。
InitializeSecurityContextの呼び出しのターゲット名はNULLです。
InitializeSecurityContextの呼び出しのターゲット名は、適切に形成されたSPN、UPN、またはNetBIOSスタイルのドメイン名ではありません。
後者の2つのケースは、Negotiateを直接(最初のケース)または間接的に(2番目のケースでは「プリンシパルが見つかりません」というエラーを返し、Negotiateがフォールバックするように)強制的にNTLMにフォールバックします。
プラグインは、NTLMへのダウングレードを検出すると、警告もログに記録します。たとえば、ドメインコントローラがSPNを見つけられない場合。これらは多くの場合正当なケースであるため、警告としてのみログに記録されます。たとえば、ドメインに参加していないシステムを認証する場合などです。
NTLM停止
5000 –アプリケーションは明示的にNTLMパッケージを選択しています
重大度-エラー
アプリケーションまたはサブシステムは、AcquireCredentialsHandleの呼び出しでNegotiateではなくNTLMを明示的に選択します。クライアントとサーバーがKerberosを使用して認証することは可能かもしれませんが、これはNTLMを明示的に選択することにより防止されます。
このエラーの修正方法
このエラーを修正するには、NTLMの代わりにNegotiateパッケージを選択します。これを行う方法は、クライアントまたはサーバーが使用している特定のネットワークサブシステムによって異なります。以下に例を示します。使用している特定のライブラリまたはAPIセットのドキュメントを参照してください。
APIs(parameter) Used by Application Incorrect Value Correct Value ===================================== =============== ======================== AcquireCredentialsHandle (pszPackage) “NTLM” NEGOSSP_NAME “Negotiate”