security

Windows資格情報保護とセキュアな認証

Windowsでの資格情報保護メカニズム、Credential Guard、Windows Hello、セキュアな認証実装について解説します。

7 min read

Windows資格情報保護とセキュアな認証

はじめに

資格情報の盗難は、現代のセキュリティ侵害の主要な原因の一つです。Windowsは、資格情報を保護するための複数のメカニズムを提供しています。この記事では、これらの保護機能と安全な認証実装について解説します。

Windows資格情報の種類

認証情報の分類

種類説明保存場所
NTLMハッシュパスワードのハッシュ値SAM/LSASS
Kerberos Ticket認証チケットメモリ
Credential Manager保存された資格情報暗号化ストア
Windows Hello生体認証/ PINTPM
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
#include <windows.h> #include <wincred.h> #include <stdio.h> #pragma comment(lib, "advapi32.lib") 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
#include <windows.h> #include <wincred.h> #include <schannel.h> #include <security.h> #include <sspi.h> #pragma comment(lib, "secur32.lib") 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
#include <windows.h> #include <sspi.h> #include <security.h> #pragma comment(lib, "secur32.lib") 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
#include <windows.h> #include <wincred.h> 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資格情報の保護には、複数の層による防御が必要です。

重要な対策

  1. Credential Guard: 仮想化ベースの資格情報隔離
  2. Windows Hello: パスワードレス認証への移行
  3. LSASS保護: PPLと追加の保護設定
  4. 監査: 継続的な監視と検出
  5. 最小権限: 必要最小限の権限で運用

推奨事項

  • パスワードレス認証(Windows Hello、FIDO2)を優先
  • Credential Guardを有効化
  • LSASS保護を構成
  • 定期的なセキュリティ監査の実施
  • 資格情報盗難ツールの検出ルールを設定

これらの対策を組み合わせることで、資格情報の盗難リスクを大幅に軽減できます。