管理者権限処理の分離実装の知識マップ(知識グラフ)
この記事はWindowsアプリで管理者権限が必要な処理だけを分離する具体的な実装を扱います。UACはプロセスの整合性レベルで制御され親子プロセスは同じレベルでトークンを継承するため、同じプロセス内の一部だけを昇格させることはできず、標準ユーザーのUIと管理者権限のhelper EXEを組み合わせるAdministrator Broker Modelが基本形になります。helperの起動にはrunasを使い、UIとhelperの通信には既定ACLに頼らず明示的なPipeSecurityを設定した名前付きパイプを使い、接続元PIDの検証とhelper側で固定operationのみを受け付けるallowlistによって任意コマンド実行の口を塞ぎます。PipeOptions.CurrentUserOnlyは整合性レベルの違いまで確認してしまうため、この用途には使えません。
Administrator Broker Model
標準ユーザーで動くUIアプリと、管理者権限で動く別プロセス(helper EXE)を組み合わせ、必要な操作の瞬間だけ昇格させるWindowsアプリの権限分離モデルである。
管理者ブローカーモデル
broker EXEモデル
UAC(ユーザーアカウント制御)
管理者レベルのアクセス許可が必要な変更を行う瞬間に同意を求め、プロセスがどのアクセストークンで動作するかを制御するWindowsのセキュリティ機能である。
整合性レベル
プロセスがどのトークンで動作しているかをlow/medium/high/systemの段階で表し、保護領域への書き込み可否を左右するWindowsのアクセス制御の仕組みである。
管理者権限
コンピューター全体の設定を変更できる権限。
昇格
requestedExecutionLevel(実行レベル宣言)
アプリケーションマニフェストで宣言する、asInvoker・highestAvailable・requireAdministratorのいずれかの起動時権限レベルである。
requestedExecutionLevel(マニフェスト)
runas起動(ShellExecute Verb)
UseShellExecute=trueと組み合わせて、対象プロセスを管理者として起動するよう要求するProcessStartInfo.Verbの指定値である。
名前付きパイプ
Windowsのプロセス間通信手段のひとつ。リモートからの利用はアプリ自身のポートではなくSMB(TCP 445)を経由する。
PipeSecurityによる明示的ACL
名前付きパイプへの接続を許可するユーザーSIDを、既定のセキュリティ記述子に頼らず明示的に組み立てて限定する.NETの設定方式である。
パイプへの不正な接続
名前付きパイプの既定のセキュリティ記述子ではEveryoneや匿名にも読み取り権が入りうるため、想定していない主体がパイプへ接続できてしまう状態である。
名前付きパイプの既定ACLのリスク
接続元PIDの検証
GetNamedPipeClientProcessIdで名前付きパイプへの接続元プロセスIDを取得し、想定したプロセスからの接続かどうかを確認する検証手段である。
helperのoperation allowlist
管理者helperが受け付ける操作を、あらかじめ決めた固定の操作名(operation)だけに限定する設計である。
固定operationのみ受付
任意コマンド実行口
呼び出し元から渡された任意のコマンド文字列やパスをそのまま実行してしまう、汎用的な実行口である。
command executor化
散発的な管理者操作
普段は不要で、設定画面の特定ボタンを押した時だけ必要になるような、頻度の低い管理者権限の操作である。
Operating System Service Model
標準ユーザーのUIアプリから常駐サービスへ通信し、昇格プロンプトなしで管理者権限の処理を行わせる権限分離モデルである。
サービスモデル
常時・無人・頻繁な管理者操作
常時監視、ログ収集、バックグラウンド更新のように、無人かつ高頻度で継続的に必要になる管理者権限の処理である。
Elevated Task Model
標準ユーザーのUIアプリから、管理者権限で動くスケジュールタスクを起動して短い定型処理を行わせる権限分離モデルである。
最高権限タスクモデル
短い定型の管理者ジョブ
1回ごとに短時間で終わる、定型化された管理者権限のジョブである。
Administrator COM Object Model
標準ユーザーのUIアプリから、昇格したCOMオブジェクト(COM elevation moniker)を呼び出して管理者権限の処理を行わせる権限分離モデルである。
昇格COMモデル
既存COM前提の統合
既にCOMベースで設計された機能があり、管理者権限が必要な操作の範囲がかなり限定される統合の場面である。
PipeOptions.CurrentUserOnly
名前付きパイプへの接続を、現在のユーザー(かつ同じ昇格レベル)に限定する.NETのアクセス制御オプション。
broker EXEモデルは、管理者helperの起動時にUACの同意プロンプトまたは資格情報プロンプトを経由させることで、必要な瞬間だけ昇格させる
UACは標準ユーザーと昇格したプロセスをそれぞれmedium/high整合性レベルのトークンで区別して制御する
管理者アカウントに対するUACの同意プロンプトは、そのアカウント自身の管理者アクセストークンを求める。ただし標準ユーザーに対する資格情報プロンプトでは、要求されるのは呼び出し元とは別の管理者アカウントの資格情報であり、UAC自体は標準ユーザーでも機能するため、呼び出し元が常に管理者権限を持つとは限らない
broker EXEモデルでは、UIのマニフェストをasInvoker、helperのマニフェストをrequireAdministratorに宣言し分ける
UseShellExecute=trueとVerb="runas"でプロセスを起動すると、対象のマニフェストの要求に応じてUACの昇格プロンプトが表示される
本記事が組み立てるbroker EXE実装のように、runasと標準入出力リダイレクトの相性の悪さを避けたい場合、UIとhelperの通信には名前付きパイプなどのIPCを使う
名前付きパイプは既定のセキュリティ記述子に頼らず、明示的なPipeSecurityで接続を許可するユーザーSIDを限定して構成する
既定のセキュリティ記述子のまま名前付きパイプを使うと、Everyoneや匿名にも読み取り権が入りうる
呼び出し元ユーザーSIDにだけ接続権を与える明示的なACLを設定することで、別ユーザーからのパイプ接続を遮断できる。ただし同一ユーザーで動く別プロセスからの接続は防げないため、記事はクライアントPIDの検証を併用している
同じユーザーで動く別プロセスが先に接続してくる余地に備え、接続元PIDが想定したUIプロセスと一致するかを確認する追加防御である
同じユーザーで動く別プロセスからのなりすまし接続まで防ぎたい本記事のbroker EXE実装のように、helperはパイプ接続後にGetNamedPipeClientProcessIdで接続元PIDを検証する
helperはoperation名で処理をdispatchし、固定の操作しか受け付けないようにする
操作自体を固定しbool/enum/数値などの型付き要求だけを受け付けることで、helperが昇格した何でも実行できる口になることを防ぐ
管理者操作が散発的で必要な瞬間だけUACを出せばよい場合は、常駐serviceより管理者helper EXEを一回だけ起動する形が本記事の推奨である
常時監視やバックグラウンド更新など、常時・無人・頻繁な管理者操作にはserviceが向く
1回ごとに短く終わる定型ジョブにはElevated Task Modelが向く
既存のCOM設計があり機能がかなり限定される場合にAdministrator COM Object Modelが向く
CurrentUserOnlyは昇格レベルも確認するため、medium整合性のUIとhigh整合性のhelperという構成のbroker EXEモデルの通信には使えない
常駐serviceは管理者権限で登録され、以後は昇格プロンプトなしで管理者権限の処理を代行する
最高権限のスケジュールタスクは管理者権限で登録・実行される
昇格COMは管理者権限のプロセスとして実行される