背景
設定ファイルでユーザの偽装を行うと、アプリケーション全体が、偽装したユーザで動作します。もし、偽装したユーザが大きな特権を持っていると、OS コマンド インジェクション、バッファ オーバーフローなどの攻撃を受けた場合に、OS 全体に被害を広げることになります。特に ASP.NET など、不特定多数の人が使うことが前提ならば、最悪の場合、システムがクラッシュする危険があります。
Vista で導入された UAC の原則に倣い、最初から特権ユーザとして動作するのではなく、必要な部分で局所的に、短期間のみ、偽装するようにします。
前提条件
コード
注:VB.NET のコードを、エディタ上で C# に書き換えています。もしかすると、コンパイルエラーが出るかもしれません。
using System;
using System.ComponentModel;
using System.Security;
using System.Security.Principal;
using System.Runtime.InteropServices;
public class Impersonate : IDisposable {
// これは、enum で定義するべき。。。
private static int LOGON32_LOGON_INTERACTIVE = 2;
private static int LOGON32_PROVIDER_DEFAULT = 0;
private WindowsImpersonationContext impersonationContext;
[DllImport("advapi32.dll", SetLastError=true)]
private static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken
);
[DllImport("advapi32.dll", SetLastError=true)]
private extern static bool DuplicateToken(
IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL,
out IntPtr DuplicateTokenHandle
);
[DllImport("advapi32.dll", SetLastError=true)]
private static extern bool RevertToSelf();
[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
private Impersonate(WindowsIdentity tempWindowsIdentity) {
impersonationContext = tempWindowsIdentity.Impersonate();
}
// 本体
public static Impersonate ImpersonateValidUser(
string userName,
string domain,
string password) {
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
Impersonate retValue = null;
try {
if (RevertToSelf() == true) {
if (LogonUser(userName, domain, password,
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out token) == true) {
if (DuplicateToken(token, 2, out tokenDuplicate) == true) {
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
retValue = new Impersonate(tempWindowsIdentity);
if (retValue.impersonationContext == null) {
retValue = null;
}
}
}
}
return retValue;
} finally {
// try - finally は2重にするべき。。。
if (!tokenDuplicate.Equals(IntPtr.Zero)) {
CloseHandle(tokenDuplicate);
}
if (!token.Equals(IntPtr.Zero)) {
CloseHandle(token);
}
}
}
public virtual void Dispose() {
if (impersonationContext != null) {
impersonationContext.Undo();
impersonationContext = null;
}
}
~Impersonate() {
this.Dispose();
}
}
使う方
// using が使えるかな?
Impersonate impersonate = null;
try {
impersonate = Impersonate.ImpersonateValidUser("user", "domain", "password");
// 何らかの処理
} finally {
if (impersonate != null) impersonate.Dispose();
}
説明
Windows ユーザは、.NET Framework では WindowsIdentity クラスで表します。このクラスのメンバに、他のユーザに偽装する Impersonate メソッドがあります。
しかし、ログインするという行為は、.NET Frameowork の外にある概念です。したがって、.NET Framwework では、ユーザ名とパスワードを確認するような仕組みは提供されていません。なので、Platform Invoke(プラットフォーム呼び出し P/Invoke)を行います。
今回使用するのは、LogonUser 関数です。第2引数で指定したドメインに属する、第1引数で指定したユーザに、第3引数で指定したパスワードを使ってログインを試みます。ローカル ユーザに偽装する場合、第2引数にはマシン名を入力します。
偽装を解除するには、偽装操作を行う前の Windows ユーザーを表す、WindowsImpersonationContext クラスのメンバ メソッドである、Undo メソッドを使います。
なお、第4引数で指定した、LOGON32_LOGON_INTERACTIVE は、デスクトップとの対話処理をするために、偽装するユーザのプロファイルを読み込みます。これにより、レジストリの HKCU ハイブを操作したり、画面のあるプロセスを起動することが出来るようになります。これはまた、セキュリティ上のリスクともなり得ますので、第4引数の指定はよく注意しておこなってください。
一連の流れを説明します。
-
現在偽装していないことを確認する(RevertToSelf)
-
偽装するユーザの、ユーザ名とパスワードを確認する(LogonUser)
-
現在ログイン中のユーザのコンテキストを複製する(DuplicateToken)
-
偽装したユーザで処理を行う
-
偽装を解除する(Undo)
このうち、「偽装したユーザで処理を行う」以外は、定型処理になります。今回紹介する Impersonate クラスは、定型処理を1つのクラスとしてまとめました(Facade パターン?)。
Impersonate クラスは IDisposable インターフェイスを実装します。ここで Dispose メソッドは、「偽装を解除する」処理を行います。
懸案事項
偽装する方法は提示しましたが、安全に偽装するユーザのアカウント情報を入力する方法は提示していません。Win32 API を使うか、暗号化されたファイルを使って、アカウントとパスワードを管理します。
.NET Framework 2.0 では、SecureString クラスが導入されています。これを使用することで、パスワードが、使用期間を超えてメモリ上に残ることを回避できます。String クラスでは、使用しているメモリが回収されても、同じメモリ領域が再利用されるまで、メモリの内容は保持されています。
参考
修正履歴
2006/11/10 |
タイトル: ACCESSファイルにアクセス不可(VSUG 質問スレッド)での SRさんのご指摘により、VB → C# 変換時のミスを修正
-
変数宣言で、As が残っていた
-
If が、何ヶ所か大文字だった
-
デストラクタの宣言を間違っていた
-
Strict On にしていなかったらしいorz
-
コンパイルしていないから、out パラメータがいるというのが・・・
-
nanka,wakarankedo,eraikanchigaiwohitotsu... |
ビルドが通るところまで確認。
投稿日時 : 2006年6月6日 22:24