解説

インサイド .NET Framework [改訂版]

第6回 コード・アクセス・セキュリティ(その1)

吉松 史彰
2003/08/06 改訂(改訂前の記事はこちら
Page1 Page2 Page3

 本稿は、2002/09/27に公開された記事を、.NET Frameworkの新しいバージョンである「.NET Framework 1.1」に対応させ、全面的に加筆・修正を行った改訂版です。

はじめに

Back Issue
1
マネージ・コード/アセンブリ/モジュール
2 アセンブリのアイデンティティ
3 アセンブリのロード
4 アセンブリとバージョン管理
5 アセンブリのロードとセキュリティ

 共通言語ランタイム(CLR)は、プログラマーが開発したコードを実際に実行する前に、実にさまざまな作業を行う。前回はコードがメモリ上にロードされるまで、およびロードされた後、改ざんのチェックのために電子署名を活用する手順を説明した。前回解説した手順は、アプリケーションがほかのアセンブリをロードするたびに行われる。例えばa.exeアプリケーションが、

b, version=1.0.0.0, culture=netural, publickeytoken=xxx

という名前のアセンブリをロードしようとすると、そのアセンブリのプライマリ・モジュール(b.dllなど)に格納されているコードが実行され、_CorDllMainの呼び出しが行われ、電子署名のチェックが行われる。

 CLRがアセンブリをロードするタイミングで行う作業はもう1つある。それがコード・アクセス・セキュリティ機構のセットアップだ。今回は、そのコード・アクセス・セキュリティについて解説しよう。

Windowsのセキュリティ機構

 これまでのWindowsベース・アプリケーションにとって、「セキュリティ」とは、すなわちユーザーの認証(Authentication)と承認(Authorization)のことだった。あなたは毎朝会社に出社し、マシンを立ち上げてWindowsのドメインにログオンするためのユーザー名とパスワードを入力する。Windowsは最初のプロセスuserinit.exeを起動し、それがexplorer.exeという名前のデフォルトのシェルを起動する。このときexplorer.exeにはセキュリティ・トークンが割り当てられる。このトークンは、さっきログオンしたあなたに関する情報が書いてあるIDカードのようなものだ。これ以降ログオフするまで、このトークンがあなたに付いて回る。Wordを起動すればWordにもこのトークンが付けられる。Wordでファイルを開こうとすると、NTFSファイル・システム・ドライバがトークンとファイルに付いているACL(Access Control List)をチェックする。何らかのアクセスが許可されれば、それに応じた権限をもってファイルが開かれ、あなたはWord上で文書を見ることになる。あるファイルをWordやExcelが開けるかどうかは、Wordに付いているセキュリティ・トークンにのみ依存する。つまり、これまでのWindowsセキュリティにおいては、「誰がファイルを開こうとしているのか」が重要だったということだ。

 このセキュリティ機構を破って、本来見ることができないファイルを見るために、クラッカーたちはあなたになりすまそうとする。常駐プログラムのセキュリティ・ホールを突いてパスワードを盗んだり、ネットワークを監視して、流れるパケットからパスワードを搾り出したり、プロセスそのものを乗っ取ったりして、なんとかあなたになりすますのがこれまでの手口だった。だが最近では、もっとお手軽な方法が浸透してきている。それは「あなたにプログラムを実行させる」というやり方だ。わざわざ手間をかけてあなたになりすまさなくても、あなたにどうにかして悪意のあるプログラムを実行してもらえばいいのだ。いくら強固なパスワードを使っていても、週に1度パスワードを変えていても、正当なユーザーがプログラムを実行することは防げない。あなたがWordで開いたファイルは、Wordが見ているも同然だ。「あなたが見ることができるファイル」は、あなたが実行した悪意のあるプログラムからも見ることができる。実に当たり前の話だ。

 それでも今までは、ユーザーがプログラムを実行するためには、まずそのプログラムをインストールするという作業が必要だった。インストールして実行するという2段構えになっていたことで、立ち止まって冷静に考える機会が与えられていた。ところが、プログラムをインストールしてまわるのはメンドクサイというケーブル・モンキー、もとい、ネットワーク管理者たちの要望によって、プログラムはインストールしなくても、ダウンロードすれば即実行できるようになってしまった。Webページ上のJavaScriptしかり、JavaアプレットやActiveXコントロールしかりである。そして、Webサイトには誘惑があふれかえっている。毎日届く電子メールにも心躍る文句が並ぶ。結果としてユーザーは自分の意思に反して、悪意のあるプログラムを実行させられてしまう。

 そういうわけで、赤ずきんちゃん(無垢なエンド・ユーザー)の前には、狼(悪意のプログラム)がおばあさんの服(電子メールに添付されたファイル)を着て、あの手この手で実行してもらえるように誘惑をしている。たいていの赤ずきんちゃんは狼の誘惑に屈して添付ファイルを実行してしまう。その結果、私たちの周りには“Klez”や“Frethem”があふれているというわけだ。

コード・アクセス・セキュリティの動機

 ネットワーク経由など、そのマシンのローカル・ディスクではない別の場所から飛んできて、ローカルのCPU上で実行されるコードのことを「モバイル・コード」と呼ぶ。モバイル・コードはしばしば、ユーザーが意図していないにもかかわらず実行される。このため、正当なユーザーの権限で、悪意あるコードを実行させるための絶好のプラットフォームになってしまっているのだ。実行しているのが正当なユーザーである以上、モバイル・コードの前ではユーザー名とパスワードの組み合わせは無力だ。

 この問題は、「誰が」プログラムを実行しようとしているか、だけではなく、「どんな」プログラムを実行しようとしているのか、によって実行権限を許可したり拒否したりすることができれば解決できる。実はこの発想は新しくない。Javaアプレットが実行されるセキュリティ空間「サンドボックス」を覚えているだろうか。サンドボックスの発想は、「Webブラウザ上で実行されるアプレットは、Webブラウザを誰が実行しているかに関係なく危険になる可能性がある」という思想の基に、その中で実行されるコードにできるだけ少ない権限だけを与えるようにする環境だ。また、ActiveXコントロールに適用されたAuthenticode技術では、Microsoftは「Webブラウザ上で実行されるActiveXコントロールといえども、それを開発した人間が信頼できれば、多くのアクセス権限を与えても問題ない」という発想をしている。一見真逆のように見えるが、JavaアプレットとActiveXコントロールには共通の思想がある。それは「誰が実行しようとしているかではなく、何を実行しようとしているかが問題だ」という視点だ。これらがコード・アクセス・セキュリティの原型だったのだ。

 Javaはその後、サンドボックスを拡張して、Java2において「プロテクション・ドメイン」という概念を確立した。Microsoftは.NET Frameworkで「コード・アクセス・セキュリティ」を現実のものにした。

コード・アクセス・セキュリティの動作

 例えば次のようなC#のコードを考えてみよう。

using System.IO;
public class FileReader {
  private string filepath;
  public FileReader(string FilePath) {
    filepath = FilePath;
  }
  public string ReadWhole() {
    StreamReader sr = File.OpenText(filepath);
    string data = sr.ReadToEnd();
    sr.Close();
    return data;
  }
}
ファイルを読み取るクラスを記述したサンプル・コード(FileReader.cs)

 これはファイルを読み取るだけのクラスである。これをDLLのシングル・ファイル・アセンブリとしてコンパイルする。

C:\TEMP>csc.exe /t:library FileReader.cs

 さらに、上記のアセンブリを利用するアプリケーションを作成する。

using System;
class MainClass {
  static void Main(string[] args) {
    FileReader reader = new FileReader(args[0]);
    Console.WriteLine(reader.ReadWhole());
  }
}
上記のクラスを利用するアプリケーションのサンプル・コード(MainClass.cs)

 これをEXEのシングル・ファイル・アセンブリとしてコンパイルする。

C:\TEMP>csc.exe /r:FileReader.dll MainClass.cs

 (設計上の問題を除けば)一見何の問題もなく実行できるコードができたようだ。実際に実行してみれば、正しく結果が返されるだろう。

C:\TEMP>MainClass.exe C:\Boot.ini
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Microsoft Windows XP Professional" /fastdetect
上記アプリケーションの実行例

 さて、それでは同じコードを次のように実行してみよう。まず、MainClass.exeとFileReader.dllが配置されているフォルダを共有する。

C:\TEMP>net share SHARED=C:\TEMP
SHARED が共有されました。

 次に、その共有フォルダにUNC名を使ってアクセスする。

C:\TEMP>\\crystalriver\SHARED\MainClass.exe C:\Boot.ini

 結果は実行時例外となる。

共有フォルダにアプリケーションを配置し、UNC名により実行した場合に出力される例外
 
ハンドルされていない例外 : System.Security.SecurityException: 種類 System.Security.Permissions.FileIOPermission, mscorlib, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 のアクセス許可の要求に失敗しました。
   at System.Security.CodeAccessSecurityEngine.CheckHelper(PermissionSet grantedSet, PermissionSet deniedSet, CodeAccessPermission demand, PermissionToken permToken)
   at System.Security.CodeAccessSecurityEngine.Check(PermissionToken permToken, CodeAccessPermission demand, StackCrawlMark& stackMark, Int32 checkFrames, Int32 unrestrictedOverride)
   at System.Security.CodeAccessSecurityEngine.Check(CodeAccessPermission cap, StackCrawlMark& stackMark)
   at System.Security.CodeAccessPermission.Demand()
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, Boolean useAsync, String msgPath, Boolean bFromProxy)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize)
   at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize)
   at System.IO.StreamReader..ctor(String path)
   at System.IO.File.OpenText(String path)
   at FileReader.ReadWhole()
   at MainClass.Main(String[] args)

上記例外の詳細情報

 同じコードを同じユーザーが実質的に同じ場所から実行したにもかかわらず、UNC名でアクセスすると例外になってしまうのだ。これがコード・アクセス・セキュリティの威力だ。コード・アクセス・セキュリティは「誰が」コードを実行したのかを問題にしない。そのコードのアイデンティティ、つまりそのコードがどんなコードなのかだけを問題にするセキュリティ環境なのだ。


 INDEX
  解説 インサイド .NET Framework [改訂版]
  第6回 コード・アクセス・セキュリティ(その1)
  1.コード・アクセス・セキュリティの動機と動作
    2.エビデンス、ポリシー、パーミッション
    3.コード・グループとアクセス許可セット

インデックス・ページヘ  「解説:インサイド .NET Framework [改訂版]」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間