November 2017

Volume 33 Number 11

セキュリティ - 未承認の開示と使用からデータとアプリを保護する

Joe Sewell

「データ侵害」 は、ソフトウェア開発者にとって最も恐ろしい言葉です。ソフトウェアではデータが最も重要です。データを処理し、管理して、保護するのがソフトウェアです。攻撃者によってデータが侵害されるようなことがあれば、ビジネスの成功にきわめて重要な機密情報が犠牲になり、悪夢のような負債を抱え、大切なブランドへの評価が下がり、顧客のロイヤルティが失われる恐れがあります。

賢明な開発チームと運用チームなら、このリスクを管理し、HIPAA や GDPR のような規制に準拠するために、データベースと Web サービスにセキュリティ制御を実装するでしょう。このような制御には、エンドツーエンドの暗号化、厳格な ID 管理、ネットワーク動作の異常検出などがあります。セキュリティのインシデントが発生すると、こうした制御の多くはアクティブに反応します。つまり、インシデントの詳細を記録し、アラートを送信して、疑わしい要求をブロックします。

こうしたベスト プラクティスはサーバー コンポーネントを保護するものの、クライアント ソフトウェアについては同レベルには保護しません。また、データを役立てるには、権限を持つユーザーやソフトウェアに何らかの形でそのデータを開示する必要があります。クライアント ソフトウェアでもデータ侵害の発生を防ぐには、どうすればよいでしょう。

たとえば、多くの企業は、多くの場合、企業の機密データにアクセスするように設計され従業員専用のソフトウェアを作成しています。Microsoft .NET Framework を使用すると、C# や Visual Basic .NET のような言語で簡単に基幹業務 (LOB) アプリケーションを作成できますが、作成したアプリケーションにはコンパイル後も高レベルなメタデータや中間コードが含まれます。そのため、悪意のある攻撃者が承認を得ずにデバッガーを使用してそのアプリケーションを改ざんしたり、リバース エンジニアリングして、侵害したバージョンのアプリケーションを簡単に作成できます。どちらのシナリオも、サーバー コンポーネントを完全に保護しても、データ侵害につながる恐れがあります。

こうした攻撃を防ぐために取れる手段はいくつかあります。たとえば、Authenticode 署名とコードの難読化という 2 つがあります。しかし、そのほとんどは攻撃を阻止するだけの受動的なもので、攻撃を検出して報告し、それに対処することはありません。ただし、最近、Visual Studio の新しい機能によって、脅威検出機能、レポート機能、および対応機能を .NET アプリケーションに追加できるようになりました。しかも、コードの変更が不要か、ほとんど必要としません。このようなランタイム チェックはアクティブな保護手段です。セキュリティの脅威に応じてアプリケーションの動作を変更し、それによって機密データを保護できます。

今回は、代表的な LOB アプリケーションを例にして、ランタイム チェックを効果的に使う方法を紹介します。データ侵害を引き起こすために攻撃者がデバッガーを利用する方法を説明し、ランタイム チェックでこのような侵害から保護する方法について詳しく取り上げ、これらの制御が階層型保護アプローチの中で果たす役割を示します。

サンプル ソリューション

クライアントでデータ侵害が発生するしくみの説明を簡単にするために、サンプル LOB ソリューションを用意しました。これは、bit.ly/2yiT2eY (英語) からダウンロードできます。このソリューションのさまざまな部分をビルド、実行、および使用する手順については、サンプル コードの README で確認できます。

このソリューションには次の 4 つのコンポーネントが含まれています。

AdventureWorks2014 データベース: これは、AdventureWorks2014 OLTP デバイス サンプルを含む Microsoft SQL Server データベースです。このサンプルは bit.ly/2wM0yy0 からダウンロードできます。

AdventureWorksSalesService (セールス サービス): これは、データベースの顧客データを開示する ASP.NET Web サービスです。開示するデータには、クレジット カードのような機密データも含まれます。このコンポーネントのセットアップを容易にするために、サンプル コードではほとんどのセキュリティ制御を省略しています。ただし、今回の目的に合わせて、サービスでは以下を実装しているものとします。

  • 2 要素認証 (ユーザー パスワードに加えて、ログイン時にユーザーの電話に SMS を送信することによる認証)
  • 時間制限セッション
  • すべての要求と応答の SSL 暗号化

AdventureWorksSalesClient (セールス クライアント): これは、顧客データを操作するためにセールス サービスに接続する Windows Presentation Foundation (WPF) デスクトップ クライアントです。今回は、このコンポーネントが最も大きく関係します。

営業部門の従業員がセールス クライアントを実行するときは、LoginDialog を使ってログインします。これにより認証後のセッションが開始され、CustomerWindow が開きます。このウィンドウに顧客の名前を表示して編集できます。EmailWindow、PhoneWindow、または CreditCardWindow を開いて顧客の機密データを編集することもできます。一部の共通関数は、Utilities という静的クラスでも提供されます。

Application Insights: サンプルの実行には必要ありませんが、セールス サービスとセールス クライアントでは、使用状況とエラー利用統計情報を Application Insights に送信できます。今回説明するランタイム チェックを使用すると、セールス クライアントの利用統計情報にセキュリティ インシデント レポートも含まれます。

今回はセールス クライアントを保護することに注目します。データベースとセールス サービスは既に保護されているものとします。当然、実際のシナリオでは安全であることを前提にはできませんが、これはポイントをはっきり示すためです。これは、サーバー セキュリティについて「すべてのことを適切に対処」しているとしても、クライアント ソフトウェア経由でデータ侵害が起きる可能性があるということを示しています。

また、顧客の名前は機密データに該当しないものとします。ここでは、電子メール アドレス、電話番号、およびクレジット カードを保護をすることに重点を置きます。実際のシナリオでは、顧客の名前も機密データと見なされるでしょう。小売店の住所などは機密データに該当しません。

デバッガーを使ったデータ侵害

デバッガーは優れた開発ツールです。デバッガーを使うと、重大なロジック エラーを発見し、厄介な制御フロー シナリオをステップ実行して、クラッシュ ダンプを診断できます。しかし、あらゆるツールと同様、デバッガーも悪意のある目的に使用される恐れがあります。

AdventureWorks イントラネットに悪意のある攻撃者がいるとしましょう。それは、復讐心を燃やす従業員の場合もあれば、外部の委託業者や、そのイントラネットに承認なくアクセスできるようになった外部の攻撃者の場合もあります。この攻撃者には、データベースやセールス サービスへのアクセス権はありません。しかし、営業部門の従業員のノート PC にはアクセスできます。確かに、これはセキュリティー上問題です。しかし、セールス サービスでは 2 要素認証を実装していて、攻撃者はその従業員の電話にはアクセスできないため、顧客データは安全だと考えています。本当にそのとおりなのでしょうか。

実際は違います。攻撃者は、営業部門の従業員がセールス クライアントを使用してログインするのを待機し、ログインされたら手動またはスクリプトによってそのクライアント プロセスにデバッガーをアタッチできます。セールス クライアントは .NET アプリケーションなので、デバッガーを使うことでセッション トークンを含む多数の高レベルの情報が明らかになりれます。デバッグ シンボル (PDB ファイルなど) がなくてもそれは変わりません。

図 1 に、このシナリオを示します。WinDbg デバッガー (bit.ly/2sh4clf、英語) と Psscor4 拡張機能 (bit.ly/2hbG2kk、英語) を使用して、実行中のセールス クライアント プロセスのメモリ内のさまざまな .NET オブジェクトをダンプしています。最終的に、AuthToken オブジェクトが見つかり、その HashField プロパティの値がダンプされます。

セールス クライアントのセッション トークンを明らかにする WinDbg

図 1 セールス クライアントのセッション トークンを明らかにする WinDbg

セッション トークンがあれば、攻撃者はセールス サービスに対し、従業員の名前で認証済みの要求を実行できます。攻撃者は、セールス クライアントのデバッグや操作を続ける必要はありません。セッション トークンを入手したら、Web サービスに直接アクセスし、そのトークンを使用してデータ侵害を引き起こすことができます。

悪意のある攻撃者によって未承認の方法でセールス クライアントが使用される可能性があるシナリオは他にもあります。

機密データの直接操作: 上記のシナリオはセッション ハイジャック攻撃でしたが、セールス クライアントでは通常の操作の一環として機密データ (クレジット カードなど) にアクセスするため、そうしたデータもデバッガーにより表示される可能性があります。攻撃者によってアプリケーションの異常動作が引き起こされたり、データベース内のデータが変更されることさえあります。

リバース エンジニアリング: 攻撃者は、クライアント自体を実行してデバッガーをアタッチし、クライアントの動作のしくみを明らかにすることもできます。.NET アプリケーションを簡単に逆コンパイルできることも相まって、セールス クライアントまたはセールス サービスに関するセキュリティ上の弱点やその他の詳細など、攻撃の計画に役立つ可能性のある情報を見つけ出せる場合があります。

改ざん: 攻撃者がアプリケーションをリバース エンジニアリングでき、なおかつ従業員のファイル システムにアクセスできる場合は、本物のセールス クライアントを改ざんしたものに置き換え、従業員がログインしたときにデータを密かに抽出または操作することができます。

他のアプリケーションも、さまざまな形でデバッガーに対して脆弱である可能性があります。たとえば、営業活動を追跡するために従業員の位置情報を報告するアプリケーションを操作し、不正確なデータを提供するように変更することもできるでしょう。また、ゲームにデバッガーを使用することで、重要な戦略的情報が明らかになる場合もあります。

ランタイム チェックの概要

ランタイム チェックは、2003 年から Visual Studio に搭載されている保護ツール PreEmptive Protection - Dotfuscator Community Edition (CE) の新機能です (/ja-jp/visualstudio/ide/dotfuscator/)。Dotfuscator CE は .NET アセンブリの中間コードを難読化できるツールとして知られていますが、難読化は今回のテーマではありません。代わりに、ランタイム チェック (以降「チェック」) を使用して、実行中のセールス クライアントを保護する方法を紹介します。

チェックは、Dotfuscator から .NET アプリケーションに挿入できるビルド済みの検証です。挿入後、デバッグや改ざんなどの未承認の使用をアプリケーションで検出できるようになります。その名前にもかわらず、チェックではこのような状態を検出するだけでなく、アプリケーションを終了するなど、事前に指定した方法で対処することも可能です。また、アプリケーション コードを呼び出して、チェックの結果に応じてカスタム動作を実行することもできます。これらのレポート機能と対応機能は、チェックごとに構成可能です。そのため、すべてのアプリケーションで同じ方法で未承認の使用を検出し、同時にアプリケーション単位に異なる方法で検出結果に対応できます。

コード サンプルには、Dotfuscator.xml 構成ファイルを含めています。このファイルを使用することで、未承認のデバッグと改ざんからセールス クライアントを保護するように Dotfuscator に指示できます。ここからは、この構成ファイルを作成した方法、選択した対象、および読者が Dotfuscator を同じように構成して自身のアプリケーションを保護する方法について説明します。

ツールのセットアップ

Dotfuscator CE に着手する最も簡単な方法は、Visual Studio のクイック起動機能 (Ctrl キーを押しながら Q キーを押します) を使用して、「dotfuscator」を検索することです。 Dotfuscator がインストールされていない場合は、[インストール] に [PreEmptive Protection - Dotfuscator] オプションが表示されます。そのオプションを選択して、適切なダイアログを確認します。

Dotfuscator がインストールされたら、この検索をもう一度行うことで、[ツール] に [PreEmptive Protection - Dotfuscator] を起動するオプションが表示されます。そのオプションを選択することで、Dotfuscator を使い始めることができます。初めて使用すると標準ダイアログがいくつか表示され、その後 Dotfuscator CE の UI が開きます。

**重要: 今回説明しているサンプルに含めた保護には、Dotfuscator CE バージョン 5.32 以降が必要です。インストールされているバージョンを確認するには、[ヘルプ]、[バージョン情報] の順に選択します。それ以前のバージョンを使用している場合は、bit.ly/2fuUeow (英語) から最新バージョンの Community Edition をダウンロードしてください。

Dotfuscator は特別な構成ファイルで動作します。この構成ファイルでは、保護対象にする必要があるアセンブリと保護の適用方法を指定します。Dotfuscator は読み込まれた新しい構成ファイルを使用して開始されます。セールス クライアントの構成ファイルを使用するように調整するには、次の手順を使用します。

  1. まず、新しい構成ファイルを AdventureWorksSalesClient\Dotfuscator.xml として保存します。
  2. 次に、セールス クライアントのアセンブリがある場所を Dotfuscator に指示します。Dotfuscator の入力画面に切り替え、緑色の正符号アイコンをクリックします。[Select Input] (入力の選択) 参照ダイアログで、AdventureWorksSalesClient\bin\Release ディレクトリに移動し、ファイルを選択せずに [Open] (開く) をクリックします。
  3. Dotfuscator でディレクトリ全体を Release という名前の入力として追加します。ツリー ノードを展開し、AdventureWorksSalesClient.exe アセンブリがあることを確認します。
  4. 次に、成ファイルを移植可能にします。つまり、環境内で絶対パスを指定しません。Release ノードを選択し、鉛筆のアイコンをクリックして、絶対パスを ${configdir}\bin\Release に置き換えます。${configdir} は、構成ファイルを保持するディレクトリを表す Dotfuscator マクロです。
  5. 最後に、今回は Dotfuscator のコード難読化機能とは関係がないので、Dotfuscator ナビゲーション リストの [Renaming] (名前の変更) 項目を右クリックし、[Enable] (有効) チェック ボックスをオフにすることで、この機能を無効にします。

チェック挿入の構成

Dotfuscator では、[Injection] (挿入) 画面の [Checks] (チェック) タブからチェックを構成できます。ただし、構成での選択対象は、保護しようとしているアプリケーションの種類に応じて変わります。セールス クライアントのサンプル用に選んだ機能と構成については説明しますが、すべての機能と設定は掲載していません。

このサンプルでは、次の 3 つのチェックを構成しています。

  • デバッガーの未承認の使用を検出する 2 つのデバッグ チェック。
    • 前述のセッション ハイジャック シナリオを検出する「ログイン」デバッグ チェック
    • クライアントでの機密データに対する読み取り/書き込みに使用されているデバッガーを検出する「クエリ」デバッグ チェック
  • 変更されたアプリケーション バイナリが使用されていることを検出する 1 つの改ざんチェック

図 2 に、チェックと、チェックで操作するアプリケーション コードの大要を示しています。ここからの 3 つのセクションでは、これらの各チェックの目的と構成について説明します。

セールス クライアントと挿入されたランタイム チェック

図 2 セールス クライアントと挿入されたランタイム チェック

ログイン デバッグ チェックの構成

最初のこのログイン デバッグ チェックは、セッション ハイジャック シナリオに対処するものです。認証プロセス中にデバッガーが存在するかどうかを検出し、デバッガーがある場合はアプリケーションに通知します。アプリケーションは、そのインシデントを Application Insights に報告し、その後、簡単には説明できない方法でエラーを報告します。

このログイン デバッグ チェックを追加するには、[Add Debugging Check] (デバッグ チェックの追加) ボタンをクリックします。これにより、新しい構成ウィンドウが表示されます。このログイン デバッグ チェックは図 3 のように構成します。

ログイン デバッグ チェックの構成

図 3 ログイン デバッグ チェックの構成

場所: まず、アプリケーション コード内でチェックを実行する必要がある場所を指定します。このログイン デバッグ チェックではユーザーがログインしたときにデバッグを検出する必要があるため、[Locations] (場所) ツリーの LoginDialog.ConfirmLogin メソッドのチェック ボックスをオンにします。

チェックが実行されるのは、対応する場所が呼び出されたときのみであることに注意してください。攻撃者が後からデバッガーをアタッチした場合、このログイン デバッグ チェックではデバッガーを検出できません。ただし、この問題には後ほどクエリ デバッグ チェックで対処します。

アプリケーション通知: アプリケーションから報告し、カスタマイズした方法で対応できるように、チェックの実行後にアプリケーション コードに通知できます。この通知を受け取るコード要素は、アプリケーション通知シンクと呼ばれ、次のチェック プロパティを使用して構成します。

  • ApplicationNotificationSinkElement: コード要素の種類 (フィールドやメソッドなど)
  • ApplicationNotificationSinkName: コード要素の名前
  • ApplicationNotificationSinkOwner: コード要素を定義する型

3 つのチェックすべてについて、Application Insights にインシデントを報告するためにこの機能を使用します。また、このログイン デバッグ チェックでは、Dotfuscator により挿入される既定の対応ではなく、独自の対応を行います (別のチェックでは既定の対応を使用します)。今回の対応では、ログインには成功しても、その直後にアプリケーションをクラッシュさせます。検出と対応を分離することで、攻撃者がこの制御を発見して回避するのが難しくなります。

この対応を実現するため、ブール型のフィールド isDebugged を Login­Dialog クラスに追加し、これをログイン デバッグ チェックのシンクとして構成します。ログイン デバッグ チェックが実行されると (つまり、アプリケーションが LoginDialog.ConfirmLogin を呼び出すと)、デバッガー検出の結果がこのフィールドに格納されます。デバッガーが検出された場合は true が格納され、それ以外は false が格納されます。

チェックの場所からシンクへのアクセスと書き込みが可能でなければならないことに注意してください。場所とシンクはどちらも LoginDialog クラスのインスタンス メンバーなので、このルールは満たされています。

次に、このフィールドを CustomerWindow コンストラクターに渡すように LoginDialog.RunUserSession を変更します。

// In LoginDialog class
private void RunUserSession(AuthToken authToken) 
{
  // ...
  var customerWindow = new Windows.CustomerWindow(clients, isDebugged);
  // ...
}

その後、CustomerWindow コンストラクターで固有のフィールド CustomerWindow.isDebugged を設定し、インシデントを Application Insights に報告します。

// In CustomerWindow class
public CustomerWindow(Clients clients, bool isDebugged)
{
  // ...
  this.isDebugged = isDebugged;
  if (isDebugged)
  {
    // ClientAppInsights is a static class holding the Application 
    // Insights telemetry client
    ClientAppInsights.TelemetryClient.TrackEvent(
      "Debugger Detected at Login");
  }
  // ...
}

最後に、このフィールドをさまざまなイベント ハンドラーに読み取るコードを追加します。これは、次のようになります。

// In CustomerWindow class
private void FilterButton_OnClick(object sender, RoutedEventArgs e)
{
  // ...
  if (isDebugged) { throw new InvalidCastException(); }
  // ...
}

フィールド名 isDebugged からその意図を簡単に推測できる点については後ほど取り上げます。

クエリ デバッグ チェックの構成

セールス クライアントの実行中にはいつでもデバッガーがアタッチされる可能性があるため、ログイン デバッグ チェックだけでは不十分です。クエリ デバッグ チェックを使用すれば、アプリケーションからクレジット カード番号などの機密データにアクセスする際に、デバッガーの有無を確認して、その不十分さを補います。こうしたデータは機密性が高いので、ログイン デバッグ チェックのように検出、報告、対応を分離するわけにはいきません。分離すると、攻撃者によってこうしたデータを確認される恐れがあるためです。代わりに、クエリ デバッグ チェックではデバッガーが検出されたらインシデントを報告し、即座にアプリケーションを終了します。

この 2 つ目のデバッグ チェックは 1 つ目と同じ方法で追加します。ただし、このクエリ デバッグ チェックは図 4 のように構成します。

CreditCardWindow.UpdateData の場所を示すクエリ デバッグ チェックの構成 (他の場所は非表示)

図 4 CreditCardWindow.UpdateData の場所を示すクエリ デバッグ チェックの構成 (他の場所は非表示)

場所: 今回のシナリオには、電子メール アドレス、電話番号、クレジット カードの 3 種類の機密データがあります。さいわい、1 つのチェックに複数の場所を選択できます。今回の場合、その場所は EmailWindow.UpdateData、PhoneWindow.UpdatePhones、および CreditCardWindow.Update­Data です。これらのいずれかが呼び出されると必ずチェックが実行されます。つまり、3 種類の機密データすべてに対して、チェック プロパティを 1 セットのみ構成する必要があります。

アプリケーション通知: 場所が複数ある場合、アプリケーション通知シンクの動作が変更されます。ログイン デバッグ チェックでは、LoginDialog.isDebugged フィールドをシンクとして指定できました。ログイン デバッグ チェックの唯一の場所である LoginDialog.ConfirmLogin からこのフィールドへのアクセスが可能だったためです。今回は、各場所からシンクにアクセスできる必要があります。

特に、Application­NotificationSinkOwner プロパティが空の場合、シンクではチェックの場所を定義している型が既定で使用されます。このクエリ デバッグ チェックには複数の場所があるため、シンクはクエリ デバッグ チェックがトリガーされた場所によって変わります。今回の例では、Application­NotificationSinkOwner は空のままにして、他の ApplicationNotificationSink プロパティを ReportDebugging という名前のメソッドに設定しました。

次の EmailWindow.ReportDebugging について考えます。

// In EmailWindow class
private void ReportDebugging(bool isDebugging)
{
  if (isDebugging)
  {
    ClientAppInsights.TelemetryClient.TrackEvent(
      "Debugger Detected when Querying Sensitive Data",
      new Dictionary<string, string> { { "Query", "Email Addresses" } });
    ClientAppInsights.Shutdown();
  }

アプリケーションから EmailWindow.UpdateData メソッドを呼び出すと、クエリ デバッグ チェックが実行され、デバッグが検出された場合は引数 true、それ以外の場合は引数 false をそれぞれ指定してこの ReportDebugging メソッドが呼び出されます。

アプリケーション コードから PhoneWindow.UpdatePhones または CreditCardWindow.UpdateData を呼び出す場合も動作は同じです (ただし、クエリ デバッグ チェックにより呼び出されるメソッドが、PhoneWindow または CreditCardWindow で別々に定義されている場合は除きます)。これらのメソッドは若干異なる方法で実装しますが、いずれも ReportDebugging という名前にして、ブール型の引数を 1 つ受け取り、値を返しません。

アクション : デバッガーがアタッチされている場合にアプリケーションを終了するため、Action プロパティを Exit に設定します。これにより、未承認状態がクエリ デバッグ チェックにより検出された場合、アプリケーションを終了するコードを挿入するように Dotfuscator に通知されます。チェックはアプリケーションに通知した後にアクションを実行することに注意してください。そのため、このシナリオでは、インシデント レポートが送信されてからアプリケーションが終了することになります。

改ざんチェックの構成

最後に、リバース エンジニアリング シナリオに対処するために改ざんチェックを追加します。[Add Tamper Check] (改ざんチェックの追加) ボタンをクリックし、図 5 のように、新しい改ざんチェックを構成します。

LoginDialog.ConfirmLogin の場所を示す改ざんチェックの構成 (他の場所は非表示)

図 5 LoginDialog.ConfirmLogin の場所を示す改ざんチェックの構成 (他の場所は非表示)

場所: クエリ デバッグ チェックと同様、改ざんチェックに複数の場所を指定します。LoginDialog.ConfirmLogin、CustomerWindow.UpdateData、および Utilities.ShowAndHandleDialog の 3 つです。

デバッグ チェックを使用する場合、アプリケーションの実行中にいつでもデバッガーがアタッチされる可能性があるため、複数の場所を用意することが重要になります。しかし、改ざんチェックではアプリの実行全体で 1 つの結果しかもたらされません。つまり、起動時に読み込まれたアセンブリが、変更されたものかどうかということです。場所は 1 つあれば十分ではないでしょうか。実際のところ、この改ざんチェックは改ざん済みのバイナリを阻止するためのものなので、攻撃者がある場所から改ざんチェック自体を削除できるシナリオを考慮する必要があります。複数の場所を用意することで、改ざんに対するアプリケーションの抵抗力がより高まります。

場所の 1 つである LoginDialog.ConfirmLogin が、ログイン デバッグ チェックの場所と同じであることにお気付きかもしれません。Dotfuscator では、同じ場所にさまざまな種類のチェックを複数挿入できます。今回の場合、ユーザーがログインすると、デバッグと改ざんの両方が確認されます。

アプリケーション通知: アプリケーション通知シンクについては、この例ではすべての場所に対してシンクを 1 つだけ用意する方が適切だと判断しました。これは、クエリ デバッグ チェックとは異なり、改ざんチェックがトリガーされた場所のレポートに関してはそれほど気にしていないためです。

Utilities.ReportTampering メソッドをシンクとして定義します。場所ごとにコンテキストが変わるため、シンクが静的であることを宣言し、各場所からシンクにアクセスできるようにする必要があります。このメソッドは次のように定義しています。

// In Utilities static class
internal static void ReportTampering(bool isTampered)
{
  if (isTampered)
  {
    ClientAppInsights.TelemetryClient.TrackEvent(“Tampering Detected”);
    ClientAppInsights.Shutdown();
  }
}

改ざんチェックのいずれかの場所が呼び出されると必ず、Dotfuscator による処理が行われてからアプリケーションに変更が加えられているかどうかを、改ざんチェックによって判断します。その後、変更が検出された場合は true パラメーターを、それ以外の場合は false パラメーターをそれぞれ指定して ReportTampering が呼び出されます。

アクション : アプリケーションが変更されている場合、続行するのは危険です。この改ざんチェックの Action プロパティは Exit に構成します。そのため、改ざんの検出時はアプリケーションを終了します。

チェックの挿入

チェックを構成したら、Dotfuscator でこれらのチェックをアプリに挿入できるようになります。それには、Dotfuscator CE で AdventureWorksSalesClient\Dotfuscator.xml 構成ファイルを開き、[Build] (ビルド) ボタンをクリックします。

Dotfuscator によってセールス クライアントのアセンブリが処理され、チェックが挿入されて、保護されたセールス クライアントが AdventureWorksSalesClient\Dotfuscated\Release に出力されます。保護されていないアプリは、AdventureWorksSalesClient\bin\Release にそのまま残ります。

チェックのテスト

あらゆるセキュリティ制御と同様、制御を導入したらアプリケーションの動作をテストすることが重要です。

通常のシナリオ: アプリの正当なユーザーにランタイム チェックによる影響が及んではなりません。保護されたセールス クライアントを通常どおりに実行し、予想外のクラッシュ、アプリケーションの終了、または Application Insights イベントが発生しないことを確認します。

未承認のシナリオ: アプリケーションが未承認の方法で使用された場合に、チェックが期待どおりに動作することも確かめる必要があります。サンプル コードの README には、デバッグ チェックと改ざんチェックをテストするための詳細手順を示しています。たとえば、クエリ デバッグ チェックをテストするために、保護されたセールス クライアントを複数回実行し、WinDbg をさまざまな位置のプロセスにアタッチします。アプリケーションでは、クエリ デバッグ チェックの構成に従って正常にデバッガーの存在が報告され、対応されました。

階層型の保護戦略

ほとんどの実例では、1 つの保護方法を使用するだけでは不十分です。チェックも例外ではありません。ランタイム チェックは保護戦略の 1 つの層にすぎないものとし、エンドツーエンドの暗号化や Authenticode アセンブリ署名などの手法と併用する必要があります。複数の保護層を使用すると、1 つの層の長所で別の層の弱点を補うことができます。

今回の例では、チェックで使用する一部のアプリケーション通知シンクの名前を、isDebugged や ReportTampering のようにしています。これらの名前は、コンパイル後の .NET アセンブリにもそのまま残るため、攻撃者が簡単にこれらのコード要素の意図を理解し、対処できる可能性があります。この問題に対応するため、Dotfuscator ではチェックの挿入に加えて、アセンブリに対して名前の変更による難読化を実行することもできます。詳細については、PreEmptive Protection – Dotfuscator Community Edition ドキュメント (bit.ly/2y9oxYX、英語) を参照してください。

まとめ

今回は、ランタイム チェックと、この機能により解決される一部の問題について紹介しました。また、LOB アプリケーションを使用して、クライアント ソフトウェアでデータ侵害が発生するしくみと、そのような侵害に対して検出、報告、および対応を行うようにランタイム チェックを構成する方法について説明しました。

本稿では無料の Dotfuscator Community Edition を取り上げましたが、これと同じ考え方は商用ライセンスの Professional Edition にも適用できます。Professional Edition には、チェックの機能が追加されています (bit.ly/2xgEZcs、英語)。また、Dotfuscator の姉妹品である PreEmptive Protection - DashO (bit.ly/2ffHTrN、英語) を使用すれば、ランタイム チェックを Java アプリと Android アプリに挿入することもできます。


Joe Sewell* は、PreEmptive Solutions の Dotfuscator チームのソフトウェア エンジニアであり、テクニカル ライターでもあります。*

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Dustin Campbell に心より感謝します。


この記事について MSDN マガジン フォーラムで議論する