Windows資格情報保護とセキュアな認証
はじめに
資格情報の盗難は、現代のセキュリティ侵害の主要な原因の一つです。Windowsは、資格情報を保護するための複数のメカニズムを提供しています。この記事では、これらの保護機能と安全な認証実装について解説します。
Windows資格情報の種類
認証情報の分類
| 種類 | 説明 | 保存場所 |
|---|---|---|
| NTLMハッシュ | パスワードのハッシュ値 | SAM/LSASS |
| Kerberos Ticket | 認証チケット | メモリ |
| Credential Manager | 保存された資格情報 | 暗号化ストア |
| Windows Hello | 生体認証/ PIN | TPM |
| FIDO2キー | ハードウェアトークン | 外部デバイス |
LSASS(Local Security Authority Subsystem Service)
LSASSはWindowsの認証サブシステムです。
HLJSPOWERSHELL
# LSASSプロセスの確認
Get-Process lsass
# LSASSの保護状態確認
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v RunAsPPL
Credential Guard
概要
Credential Guardは、仮想化ベースのセキュリティ(VBS)を使用して資格情報を隔離します。
有効化
HLJSPOWERSHELL
# Credential Guardの状態確認
Get-ComputerInfo -Property DeviceGuardSmartStatus
# レジストリでの確認
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LsaCfgFlags
# グループポリシーでの有効化
# Computer Configuration -> Administrative Templates -> System -> Device Guard
# -> Turn on Virtualization Based Security -> Credential Guard Configuration
管理テンプレート(ADMX)
HLJSXML
<!-- Credential Guard グループポリシー設定 -->
<policy name="LsaCfgFlags" class="Machine"
displayName="$(string.LsaCfgFlags)"
explainText="$(string.LsaCfgFlags_Help)"
presentation="$(presentation.LsaCfgFlags)"
key="SYSTEM\CurrentControlSet\Control\Lsa">
<enum id="LsaCfgFlags" valueName="LsaCfgFlags">
<item displayName="$(string.LsaCfgFlagsDisabled)">
<value>
<decimal value="0"/>
</value>
</item>
<item displayName="$(string.LsaCfgFlagsEnabled)">
<value>
<decimal value="1"/>
</value>
</item>
</enum>
</policy>
検証
HLJSPOWERSHELL
# Credential Guardが有効かどうかの確認
msinfo32
# システム情報で「Device Guard の資格情報ガード」を確認
# PowerShellでの詳細確認
Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard
Windows Hello
概要
Windows Helloは、生体認証またはPINを使用したパスワードレス認証を提供します。
生体認証の設定
HLJSPOWERSHELL
# Windows Helloの状態確認
Get-WindowsHello
# 生体認証デバイスの確認
Get-PnpDevice -Class Biometric
# PINポリシーの確認
Get-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows\System" -Name AllowBlockingPin
PINポリシーの構成
HLJSPOWERSHELL
# PINの複雑さを要求
$Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\System"
Set-ItemProperty -Path $Path -Name "AllowBlockingPin" -Value 0
# PINの長さ設定
$Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\System"
Set-ItemProperty -Path $Path -Name "MinPINLength" -Value 6
Set-ItemProperty -Path $Path -Name "MaxPINLength" -Value 127
# 有効期限の設定
Set-ItemProperty -Path $Path -Name "ExpiryTime" -Value 0 # 0 = 無期限
Hello for Business(Azure AD)
HLJSPOWERSHELL
# Hello for Businessの状態確認
dsregcmd /status
# 主要な出力項目:
# AzureAdJoined : YES
# OnPremTgt : YES (オンプレミスのKerberos TGTあり)
Credential Manager
プログラムからの資格情報管理
HLJSC
BOOL save_credential(const wchar_t* target, const wchar_t* username, const wchar_t* password) {
DWORD passwordLen = (DWORD)(wcslen(password) * sizeof(wchar_t));
CREDENTIALW cred = {0};
cred.Type = CRED_TYPE_GENERIC;
cred.TargetName = (LPWSTR)target;
cred.UserName = (LPWSTR)username;
cred.CredentialBlob = (LPBYTE)password;
cred.CredentialBlobSize = passwordLen;
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
return CredWriteW(&cred, 0);
}
BOOL read_credential(const wchar_t* target, wchar_t** username, wchar_t** password) {
PCREDENTIALW pcred = NULL;
if (!CredReadW(target, CRED_TYPE_GENERIC, 0, &pcred)) {
return FALSE;
}
*username = _wcsdup(pcred->UserName);
*password = (wchar_t*)malloc(pcred->CredentialBlobSize + sizeof(wchar_t));
memcpy(*password, pcred->CredentialBlob, pcred->CredentialBlobSize);
(*password)[pcred->CredentialBlobSize / sizeof(wchar_t)] = L'\0';
CredFree(pcred);
return TRUE;
}
BOOL delete_credential(const wchar_t* target) {
return CredDeleteW(target, CRED_TYPE_GENERIC, 0);
}
PowerShellでの管理
HLJSPOWERSHELL
# 保存された資格情報の一覧
cmdkey /list
# 汎用資格情報の追加
cmdkey /generic:LegacyGeneric:target=MyApp /user:username /pass:password
# Windows資格情報の追加
cmdkey /add:server.example.com /user:DOMAIN\username /pass:password
# 資格情報の削除
cmdkey /delete:target=MyApp
# すべての資格情報を削除(注意!)
cmdkey /delete /ras
Kerberos認証の保護
Kerberosの概要
クライアント KDC サーバー
| | |
|---- AS_REQ --------------->| |
|<--- AS_REP (TGT) ----------| |
| | |
|---- TGS_REQ (TGT) -------->| |
|<--- TGS_REP (Service Ticket) |
| | |
|---- AP_REQ (Service Ticket) ----------------------->|
|<--- AP_REP -----------------------------------------|
Kerberosの強化
HLJSPOWERSHELL
# Kerberos暗号化タイプの確認
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters" /v SupportedEncryptionTypes
# 値の意味:
# 0x1 = DES-CBC-CRC
# 0x2 = DES-CBC-MD5
# 0x4 = RC4-HMAC
# 0x8 = AES128-CTS-HMAC-SHA1-96
# 0x10 = AES256-CTS-HMAC-SHA1-96
# AES256のみを有効化
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters" /v SupportedEncryptionTypes /t REG_DWORD /d 0x18 /f
Kerberos Armoring(FAST)
HLJSPOWERSHELL
# Kerberos Armoringの有効化
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters" /v EnableCbacAndArmor /t REG_DWORD /d 1 /f
# KDCへのArmoring要求
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters" /v ForceGroupPolicyArmor /t REG_DWORD /d 1 /f
パスワードレス認証
FIDO2セキュリティキー
HLJSPOWERSHELL
# FIDO2ポリシーの確認
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Policies\PassportForWork" -ErrorAction SilentlyContinue
# FIDO2の有効化
reg add "HKLM\SOFTWARE\Microsoft\Policies\PassportForWork" /v Enabled /t REG_DWORD /d 1 /f
証明書ベースの認証
HLJSC
BOOL authenticate_with_certificate(PCERT_CONTEXT pCertContext) {
CredHandle hCred;
TimeStamp tsExpiry;
SECURITY_STATUS Status;
SCHANNEL_CRED SchannelCred = {0};
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
SchannelCred.cCreds = 1;
SchannelCred.paCred = &pCertContext;
SchannelCred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS;
Status = AcquireCredentialsHandleW(
NULL,
UNISP_NAME_W,
SECPKG_CRED_OUTBOUND,
NULL,
&SchannelCred,
NULL,
NULL,
&hCred,
&tsExpiry
);
if (Status != SEC_E_OK) {
return FALSE;
}
// 認証処理を続行...
FreeCredentialsHandle(&hCred);
return TRUE;
}
LSASS保護の実装
Protected Process Light(PPL)
HLJSPOWERSHELL
# PPLの有効化
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v RunAsPPL /t REG_DWORD /d 1 /f
# UEFIロックでの保護(再起動後も維持)
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v RunAsPPLBoot /t REG_DWORD /d 2 /f
# 確認
reg query "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v RunAsPPL
追加のLSASS保護
HLJSPOWERSHELL
# WDigestの無効化(平文パスワードのメモリ保持を防止)
reg add "HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest" /v UseLogonCredential /t REG_DWORD /d 0 /f
# Credential Cachingの制限
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v CachedLogonsCount /t REG_SZ /d "0" /f
# この設定はドメインログオンのオフラインキャッシュに影響するため注意
セキュアな認証実装
統合Windows認証の使用
HLJSC
BOOL authenticate_to_server(SOCKET sock, const wchar_t* spn) {
CredHandle hCred;
CtxtHandle hCtx;
SEC_WINNT_AUTH_IDENTITY_W AuthIdentity = {0};
TimeStamp tsExpiry;
SECURITY_STATUS Status;
// 現在のユーザーの資格情報を使用
Status = AcquireCredentialsHandleW(
NULL,
NEGOSSP_NAME_W,
SECPKG_CRED_OUTBOUND,
NULL,
NULL,
NULL,
NULL,
&hCred,
&tsExpiry
);
if (Status != SEC_E_OK) {
return FALSE;
}
SecBufferDesc OutBuffDesc;
SecBuffer OutBuffers[1];
BYTE OutBuffer[12288];
OutBuffers[0].pvBuffer = OutBuffer;
OutBuffers[0].cbBuffer = sizeof(OutBuffer);
OutBuffers[0].BufferType = SECBUFFER_TOKEN;
OutBuffDesc.ulVersion = SECBUFFER_VERSION;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = OutBuffers;
ULONG fContextAttr;
Status = InitializeSecurityContextW(
&hCred,
NULL,
(LPWSTR)spn,
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MUTUAL_AUTH,
0,
SECURITY_NATIVE_DREP,
NULL,
0,
&hCtx,
&OutBuffDesc,
&fContextAttr,
&tsExpiry
);
if (Status == SEC_I_CONTINUE_NEEDED || Status == SEC_E_OK) {
// サーバーにトークンを送信
send(sock, (char*)OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0);
}
FreeCredentialsHandle(&hCred);
DeleteSecurityContext(&hCtx);
return (Status == SEC_E_OK);
}
アプリケーションでの資格情報プロンプト
HLJSC
BOOL prompt_for_credentials(const wchar_t* target, wchar_t** username, wchar_t** password) {
CREDUI_INFOW credui = {0};
credui.cbSize = sizeof(credui);
credui.hwndParent = NULL;
credui.pszMessageText = L"認証情報を入力してください";
credui.pszCaptionText = L"アプリケーション認証";
credui.hbmBanner = NULL;
WCHAR username_buf[CREDUI_MAX_USERNAME_LENGTH + 1] = {0};
WCHAR password_buf[CREDUI_MAX_PASSWORD_LENGTH + 1] = {0};
BOOL fSave = FALSE;
DWORD dwErr;
dwErr = CredUIPromptForCredentialsW(
&credui,
target,
NULL,
0,
username_buf,
CREDUI_MAX_USERNAME_LENGTH + 1,
password_buf,
CREDUI_MAX_PASSWORD_LENGTH + 1,
&fSave,
CREDUI_FLAGS_GENERIC_CREDENTIALS |
CREDUI_FLAGS_DO_NOT_PERSIST |
CREDUI_FLAGS_EXCLUDE_CERTIFICATES
);
if (dwErr == NO_ERROR) {
*username = _wcsdup(username_buf);
*password = _wcsdup(password_buf);
SecureZeroMemory(password_buf, sizeof(password_buf));
return TRUE;
}
return FALSE;
}
資格情報盗難の検出
イベント監視
HLJSPOWERSHELL
# 資格情報アクセスの監査
Get-WinEvent -FilterHashtable @{
LogName='Security'
ID=4624, # ログオン成功
4625, # ログオン失敗
4648, # 明示的な資格情報使用
4672 # 特権ログオン
} -MaxEvents 100
# LSASSへのアクセス検出
Get-WinEvent -FilterHashtable @{
LogName='Security'
ID=4656 # オブジェクトへのアクセス要求
} | Where-Object { $_.Message -like "*lsass.exe*" }
Sysmonでの監視
HLJSXML
<!-- LSASSへのアクセス監視 -->
<RuleGroup name="LSASS Access" groupRelation="or">
<ProcessAccess onmatch="include">
<TargetImage condition="end with">lsass.exe</TargetImage>
</ProcessAccess>
</RuleGroup>
<!-- 資格情報ダンプツールの検出 -->
<RuleGroup name="Credential Dumping" groupRelation="or">
<ProcessCreate onmatch="include">
<Image condition="contains">mimikatz</Image>
<Image condition="contains">procdump</Image>
<Image condition="contains">pwdump</Image>
<CommandLine condition="contains">sekurlsa</CommandLine>
<CommandLine condition="contains">lsadump</CommandLine>
</ProcessCreate>
</RuleGroup>
不審なプロセスの検出
HLJSPOWERSHELL
function Get-SuspiciousCredentialAccess {
$suspiciousProcesses = @(
'mimikatz',
'procdump',
'pwdump',
'gsecdump',
'cachedump',
'lsadump',
'ntdsutil',
'vssadmin'
)
Get-Process | Where-Object {
$name = $_.ProcessName.ToLower()
$suspiciousProcesses | Where-Object { $name -like "*$_*" }
} | Select-Object ProcessName, Id, Path, StartTime
}
# 実行
Get-SuspiciousCredentialAccess
ベストプラクティス
1. パスワードレス認証への移行
HLJSPOWERSHELL
# Windows Hello for Businessの有効化
$Path = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\System"
Set-ItemProperty -Path $Path -Name "AllowBlockingPin" -Value 0
# FIDO2キーのサポート
$Path = "HKLM:\SOFTWARE\Microsoft\Policies\PassportForWork"
if (!(Test-Path $Path)) {
New-Item -Path $Path -Force
}
Set-ItemProperty -Path $Path -Name "Enabled" -Value 1
2. 資格情報の最小権限
HLJSPOWERSHELL
# ローカル管理者権限の確認
Get-LocalGroupMember -Group "Administrators"
# 不要な管理者アカウントの削除
Remove-LocalGroupMember -Group "Administrators" -Member "username"
3. 定期的な監査
HLJSPOWERSHELL
# 監査スクリプト
function Audit-CredentialSecurity {
$results = @{
"Credential Guard" = (Get-ComputerInfo).DeviceGuardSmartStatus
"LSASS Protection" = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa").RunAsPPL
"WDigest" = (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest").UseLogonCredential
}
return $results
}
Audit-CredentialSecurity
4. セキュアな資格情報ハンドリング
HLJSC
// メモリ内の資格情報の安全な扱い
void handle_credentials_securely() {
wchar_t* password = NULL;
// 資格情報を取得
if (prompt_for_credentials(L"MyApp", &username, &password)) {
// 使用
authenticate(username, password);
// 即座に消去
SecureZeroMemory(password, wcslen(password) * sizeof(wchar_t));
free(password);
password = NULL;
}
}
まとめ
Windows資格情報の保護には、複数の層による防御が必要です。
重要な対策
- Credential Guard: 仮想化ベースの資格情報隔離
- Windows Hello: パスワードレス認証への移行
- LSASS保護: PPLと追加の保護設定
- 監査: 継続的な監視と検出
- 最小権限: 必要最小限の権限で運用
推奨事項
- パスワードレス認証(Windows Hello、FIDO2)を優先
- Credential Guardを有効化
- LSASS保護を構成
- 定期的なセキュリティ監査の実施
- 資格情報盗難ツールの検出ルールを設定
これらの対策を組み合わせることで、資格情報の盗難リスクを大幅に軽減できます。