January 2010
Volume 25 Number 01
クラウド セキュリティ - Windows Azure における暗号化サービスとデータ セキュリティ
Jonathan Wiggs | January 2010
Windows Azure プラットフォームの早期導入者の多くは、プラットフォームのセキュリティとその暗号化のサポートについて、現在でも多くの疑問を抱いています。この記事の目的は、Windows Azure プラットフォームにおける暗号化と関連セキュリティに関する基本的な考え方をいくつか紹介することです。この話題を詳しく解説すると書籍数冊分の内容になるため、ここでは Windows Azure に含まれる暗号化サービスと暗号化プロバイダーの例をいくつか挙げて調べていくだけにとどめましょう。その中で、Windows Azure に移行することでセキュリティに与える影響についても触れることにします。
プラットフォームやサービスの提供方法が新しくなると、新たな課題に直面することになります。従来からの問題がいくつか残っていたり、、以前に使用した解決策がいくつか依然としてうまく機能したりすることもあるでしょう。この記事で扱う話題は、格納するデータの種類や、何を保持しておくべきかに関連しているため、すべてのアプリケーション エンジニアや設計者が検討しておく必要があります。こうしたことを体系的な手法にと組み込んでおくと、開発者にも顧客にも役立ちます。
開発者のコミュニティでこうした情報が必要とされていると、私が考えるに至った理由は、ここ数か月、Azure のセキュリティ全般に関する投稿がコミュニティ サイトで増加していると感じているためです。マイクロソフトでは、Azure プロジェクトによるアプリケーション層データのセキュリティを確保する作業の一環として、暗号化を行うよう提案しています。そのためには、Windows Azure プラットフォームで製品を構築している設計者や開発者が、暗号化と .NET のセキュリティ モデルの両方を正しく理解する必要があります。
私が感じた点の 1 つは、暗号化サービスとキー記憶域に関する投稿の比率が高まっていることです。中でも特に、Windows Azure Storage サービスに関する投稿比率が高くなっています。この話題には私自身も興味があり、詳しく説明する価値のある話題だと思いました。
この記事を書き進めていく中で、暗号化サービス プロバイダー (CSP) に触れる場面が数多く出てきます。CSP とは、システム プログラム インターフェイスで提示される、暗号化の標準、アルゴリズム、および機能の実装のことです。今回の記事では、その目的に併せて、Rijndael 暗号化クラスで提供される対称暗号化アルゴリズムを使用します。
暗号化の基礎
Windows Azure SDK では、Windows Azure で提供されるサービスを開発者が統合して使用できるように、.NET のコア ライブラリが拡張されます。Windows Azure のプロジェクトやサービス内では、CSP へのアクセスが制限されません。つまり、データの暗号化と暗号化解除に関する開発の大部分は、開発者がこれまで使い慣れたアセンブリを同じように使用できます。しかし、基盤となるアーキテクチャが変更されているため、データを暗号化するタイミングや場所、およびキーを保存する場所や方法に問題が生じます。キーと機密データの保存については、この記事の後半で取り上げます。
Windows Azure では、MD5 や SHA など、すべての暗号化ハッシュ機能にもアクセスできます。これらの機能は、データ コピーの検出、ハッシュ テーブルのインデックス、メッセージの署名、パスワードの検証といった、あらゆるシステムのセキュリティ強化に不可欠です。
一貫してお勧めするのは、独自の暗号化アルゴリズムを作成したり、専用の暗号化アルゴリズムを使用したりしないことです。.NET の CSP で提供されるアルゴリズムには、実績があり、テスト済みで、何年にもわたる裏付けがあります。独自の暗号プロセスの作成に XOR を使用しても、同じアルゴリズムにはならず、同じレベルのデータ セキュリティは提供されません。
もう 1 つのお勧めは、RNGCryptoServiceProvider クラスを使用して乱数を生成することです。このようにすると、アプリケーションで生成される乱数のエントロピーが常に非常に大きくなるため、パターンの推測が困難になります。
次のコードは、32 ビットの int 値の乱数を返し、暗号化してセキュリティを確保するという要件を満たす、1 つの静的メンバーを実装します。このメンバーを実装するには、Cryptography 名前空間に含まれる RNGCryptoServiceProvider クラスのバイト ジェネレーターを使用します。
public static int GenerateRandomNumber() {
byte[] GeneratedBytes = new byte[4];
RNGCryptoServiceProvider CSP = new RNGCryptoServiceProvider();
CSP.GetBytes(GeneratedBytes);
return BitConverter.ToInt32(GeneratedBytes, 0);
}
図 1 は、Windows Azure プラットフォーム内で CSP を使用する場合の簡単な例を示しています。あらゆる Windows Azure アプリケーション内での使用を目的として公開されているパブリック メンバーが 3 つあります。1 つ目のメンバーは、バイナリ キーと初期化ベクトル (IV)、および暗号化されていないデータのバイナリ バッファーを受け取って、暗号化したデータを返します。2 つ目のメンバーは、データの暗号化を解除することで、逆の処理を実行します。3 つ目のメンバーは、そのデータの計算されたハッシュ値を返します。この例では、Rijndael CSP をプロバイダーへのマネージ アクセスに使用しています。また、データとキーをバイナリ バッファーに格納して、処理の直後にこれらのバッファーを上書きしています。この点については、後で不変性について述べる際に説明します。
図 1 簡単な暗号化
public static byte[] SampleEncrypt(byte[] dataBuffer,
byte[] Key, byte[] IV) {
MemoryStream InMemory = new MemoryStream();
Rijndael SymetricAlgorithm = Rijndael.Create();
SymetricAlgorithm.Key = Key;
SymetricAlgorithm.IV = IV;
CryptoStream EncryptionStream = new CryptoStream(InMemory,
SymetricAlgorithm.CreateEncryptor(), CryptoStreamMode.Write);
EncryptionStream.Write(dataBuffer, 0, dataBuffer.Length);
EncryptionStream.Close();
byte[] ReturnBuffer = InMemory.ToArray();
return ReturnBuffer;
}
これは、データを暗号化し、暗号化した結果をバイト配列として返す最も簡単な例です。これは一例にすぎず、すべてのセキュリティが適切に分析されていないため、セキュアな環境で使用すべきではありません。
図 2 の例は、図 1 の例とほぼ同じ構造です。このコードでは、同じキーと IV を基に、暗号化されたバイト バッファーだけをパラメーターとして受け取って、データの暗号を解除します。唯一の違いは、暗号化ストリーム作成の際に、対称復号化オブジェクトの作成を指定し、前の例のように暗号化オブジェクトの作成を指定していないことです。
図 2 簡単な復号化
public static byte[] SampleDecrypt(byte[] dataBuffer,
byte[] Key, byte[] IV) {
MemoryStream InMemory = new MemoryStream();
Rijndael SymetricAlgorithm = Rijndael.Create();
SymetricAlgorithm.Key = Key;
SymetricAlgorithm.IV = IV;
CryptoStream EncryptionStream = new CryptoStream(InMemory,
SymetricAlgorithm.CreateDecryptor(), CryptoStreamMode.Write);
EncryptionStream.Write(dataBuffer, 0, dataBuffer.Length);
EncryptionStream.Close();
byte[] ReturnBuffer = InMemory.ToArray();
return ReturnBuffer;
}
キー記憶域と保存
アプリケーション層やエンタープライズ層でのあらゆる暗号化方針と同様、暗号化と暗号化解除のインフラストラクチャは、ほんの出発点にすぎません。本当の問題は、キー記憶域とキーの保存にあります。 データを暗号化することで得られるデータのセキュリティは、暗号化に使用したキーの安全性を確保することに他ならないため、これは思ったよりもはるかに難しい問題です。私が確認したシステムでは、さまざまな場所に暗号化キーが保存されていました。ソース コードに直接保存されているキーもあれば、気の利いた名前のテキスト ファイルや、見つけにくいディレクトリに格納されたフラット ファイルに保存されているキーもありました。
クラウド環境でのキーの保存場所と管理方法について考えると、キーの保存に関する重要な疑問が浮かび上がります。キーをクラウド環境内に保存すると、クラウド自体によってセキュリティの脅威にさらされるという懸念を表明している人もいます。つまり、データに物理的にアクセスできる人がいれば、セキュリティは確保されません。既定では、ディスク上に格納されたデータは暗号化されていない可能性があります (Windows Azure でも同様です)。SQL Azure でもまだ暗号化がサポートされていないことを考えれば、このことはソリューションの計画や設計の段階で検討すべきセキュリティ上の決定事項になります。すべてのセキュリティ実装と同様に、リスクを測定し、重み付けを行い、対策を練る必要があります。
だからと言って、クラウド プラットフォーム全般 (特に Windows Azure) が本質的に安全でないと言っているのではありません。他にはどのようなオプションを利用できるでしょう。
何よりも注意が必要なのは、どのようなアプリケーションでも決して Windows Azure によって提供されるキーをデータ暗号化のキーに使用してはならないことです。たとえば、Windows Azure によって Storage サービス用に提供されるキーなどです。このようなキーは、セキュリティ上の理由から、またはなんらかの目的で侵害された場合に、簡単にローテーションできるよう構成されています。つまり、こうしたキーは将来存在しなくなったり、非常に広範囲に配布される可能性があります。
ユーザー独自のキー ライブラリを Windows Azure Storage サービスに格納することは、機密情報を保存する優れた方法です。このように保存したデータは、マルチテナント環境でもセキュリティが確保され、Storage サービスに保存したユーザー独自のキーでセキュリティが確保されていることを信頼できます。これは、Storage サービスに保存したキーを暗号化キーに使用することではありません。他の方法でファイルに保存した場合と同様に、Storage サービスに保存したキーを使用してキー ライブラリにアクセスできます。この方法は、実装が非常に簡単です。たとえば、なんらかの機密情報を保存するために、独自のキー ライブラリを単純なテキスト ファイルとして実装するとします。このような場合、キュー サービスやテーブル ストレージ サービスではなく、ブロブ (BLOB) サービス API にデータとして格納するのが最適です。Storage サービスのブロブ領域は、バイナリのオーディオやイメージだけでなく、テキスト ファイルのようなデータにも最適な場所です。Storage サービスのキュー領域は、長期間は保存しない、サイズの小さなデータ オブジェクト向けの安全なメッセージングに重点を置いています。テーブル ストレージ システムは、データベース内のリレーショナル データと同様の、特定の領域に保存してアクセスする必要がある、構造化されたデータや情報に適しています。
まず、CSP のキー コンテナーにキーを保存することから始めます。これは公開キーを格納する優れた方法で、サーバーに物理的にアクセスできなければ取得が困難です。Windows Azure では、アプリケーションとデータの場所が抽象化されるため、この方法で格納した公開キーは発見や取得がきわめて難しくなります。キー記憶域のコンテナーを作成するのは実に簡単です。キーを作成する RSA プロバイダーの使用例を紹介しましょう。キー コンテナーが既に存在すれば、そのキーが自動的にプロバイダーに読み込まれます。
CspParameters CspParam = new CspParameters();
CspParam.KeyContainerName = "SampleContainerName";
RSACryptoServiceProvider RSAProvider = new
RSACryptoServiceProvider(CspParam);
必要に応じて、他のオプションを検討することもできます。たとえば、特定のフラグを使用して、コンテナーを作成したユーザーに対してキーのセキュリティを確保できます。この処理は、次のように CspParameters の Flags メンバーを使用して実行できます。
CspParam.Flags = CspProviderFlags.UseUserProtectedKey;
ここで、Windows Azure Storage キーを使用して、ブロブ API への要求を作成します。要求自体には、署名文字列と適切な要求ヘッダーの両方が必要です。正しいヘッダーの形式は次のとおりです。
Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"
今回は、保存した機密データのセキュリティを最大限に高めるため、SharedKey 承認メソッドを使用します。ヘッダーの署名部分は、SHA256 アルゴリズムと署名のデータに対する Storage キーによって生成されるハッシュ ベースの承認コードです。このハッシュは、その後 base64 文字列にエンコードされます。署名のサンプルは次のようになります。
"PUT\n\ntext/plain; charset=UTF-8\n\nx-ms-Date:Fri, 12 Sep 2009 22:33:41 GMT\nx-ms-meta-m1:v1\nx-ms-meta-m2:v2\n/exampleaccount/storageclientcontainer/keys.txt"
既に述べたように、次は base64 でエンコードされたハッシュを生成し、この値を署名としてヘッダーに使用します。結果として、このキー ファイルにアクセスできるのは、Windows Azure クラウドのアプリケーション空間で実行されるアプリケーションを所有していて、Storage キーにアクセスできるユーザーだけになります。このように保存されるキーは、Windows Azure のフレームワークの外部からも、クラウド自体の内部からも管理できます。
キーとセキュリティの脅威
キーのセキュリティについては、簡単にでも説明しておく必要があるでしょう。これは、キーの保存方法や格納方法とはやや異なる問題です。基本的にキー自体は、エントロピーがとても高い、つまりランダム性が非常に高い文字から成る文字列です。実際、この性質がシステム内のキーを見つける一般的な攻撃方法につながることがあります。たとえば、メモリ ダンプやハード ディスク上のデータ領域を調べ、エントロピーがきわめて高い領域があれば、そこがキーを探し始める最適な場所です。
アプリケーションの要件を基に適切なセキュリティ対策を選択し、データのセキュリティを確保する以外に、どのような方法で攻撃を防ぐことができるでしょう。最初に、データの暗号解除、暗号化、およびセキュリティ保護に使用している手段は、必ずあらゆる攻撃者に熟知されているものと想定します。 これを念頭に置いて、定期的にキーを切り替えてキーの安全性を確保します。また、使う必要があるユーザーだけにキーを付与し、制御できなくなったキーが危険にさらされないようにします。
最後に、安全なデータと安全でないデータの両方について、時間をかけてデータの流れ図を作成します。データの送信先と送信方法、機密情報の格納場所、さらにデータが (パブリック ネットワークとプライベート ネットワークなどの) 境界を越える場所については特に注意します。その結果、データが危険にさらされる箇所がよくわかり、このようなリスクを計画の対象として、わかりやすいリスク軽減策を立案できます。
関連質問として、Windows Azure で SSL をサポートしているかどうかをたずねられたことがあります。簡単に答えれば、サポートされています。SSL がサポートされていなければ、Windows Azure は Web ベースのサービスやアプリケーション向けのクラウド プラットフォームとしてはほとんど役に立たないでしょう。
SQL Azure による暗号化
SQL Server 2008 のリリース時に、透過的なデータ暗号化 (TDE) という新機能が導入されました。TDE によって初めて SQL Server はデータを完全に暗号化できるようになり、必要な作業は SQL Server 2005 で利用できる限定的な暗号化に必要だった作業よりもほんのわずかに多いだけです。しかし、今後のバージョンには検討されているものの、SQL Azure Storage の初期バージョンではデータベース レベルの暗号化がまだサポートされていません。SQL Azure はポート 1433 経由および TCP 接続経由でのみアクセスできることに注意してください。現時点では、他のポートで公開できません。
データベース レベルの暗号化機能は Windows Azure にまだ統合されていないとはいえ、SQL Azure にも開発者や設計者が留意すべきセキュリティ機能がいくつかあります。まず、SQL Azure では表形式データ ストリーム (TDS) がサポートされます。つまり、ほとんどの場合、今までどおりの方法でデータベースに接続して操作できます。ADO.NET の暗号化や信頼できるサーバーの証明書の使用は、特にクラウド外部から SQL Azure データベースにアクセスする場合は、検討する価値が確実にあります。
Encrypt=True と TrustServerCertificate = False という接続プロパティを正しく組み合わせると、データ転送時のセキュリティが確保され、man-in-the-middle 攻撃を防止できます。これは、SQL Azure に接続する際の要件でもあります。接続レベルの暗号化が有効になっていない限り、SQL Azure には接続できません。
しっかりと理解しておくべき SQL Azure の 2 つ目のセキュリティ機能は、SQL Azure ファイアウォールです。ローカルのソフトウェア ファイアウォールや SQL Server のセキュリティに関するツールセットを使用したことがある方なら、このツールには非常になじみがあるでしょう。このツールでは、さまざまなソースからの接続を許可または拒否でき、接続対象を特定の IP アドレスや IP アドレスの範囲に限定できます。SQL Azure ファイアウォールは、SQL Azure ポータル経由で管理することも、sp_set_firewall_rule や sp_delete_firewall_rule など付属のストアド プロシージャを使用して master データベースで直接管理することもできます。
SQL Server のどの実装でも同じですが、ユーザー アカウント管理も厳密な管理が必要な項目の 1 つです。SQL Azure ファイアウォールは実に優れたツールですが、それだけに依存しないでください。強力なパスワードが設定され、特定の権限が構成されたユーザー アカウントを併用して、データ セキュリティ モデルを補完する必要があります。
これらの新しいツールは、SQL Azure がクラウド ベースのアプリケーション向けの非常に厳密にセキュリティ保護されたマネージ プラットフォームになるうえで大きく役立ちます。このサービスを初めて試す場合、接続するにはまず SQL Azure ファイアウォールを構成する必要があることを忘れないでください。最初は SQL Azure の Web ポータル経由でファイアウォールを構成する必要がありますが、既に説明したように、後から master データベースで直接構成することもできます。
不変性とメモリ内のリソース
不変性とは何でしょう。オブジェクト指向プログラミングにおける不変性とは、単に、オブジェクトを最初に作成した後はその状態を変更できないことを表します。Microsoft .NET Framework での具体例として文字列クラスがあります。コードから文字列値を変更すると、メモリ内にあった元の文字列が単純に放棄され、新しい値を格納するために、新しい文字列オブジェクトが作成されます。
この性質がセキュリティにとって重要なのはなぜでしょう。それは、サーバーが再起動されずにオンライン状態を保っている限り、こうした文字列がメモリ内に残っている可能性があるためです。文字列がメモリ内に残っている期間を正確に知る方法はありません。暗号化キー、暗号化したデータや暗号化を解除したデータのコピーなど、コード内で情報を格納する方法を検討する際に、このことが重要になります。このようなデータの痕跡がメモリ内に残っていると、狡猾なデータ泥棒に機密を知られる情報を残すことになります。
こうした脆弱性のため、このようなデータは常にバイト配列などバッファーに格納することをお勧めします。そうすれば、情報の処理が完了した直後に、バッファーをゼロなどのデータで上書きして、データがメモリに残らないようにすることができます。
Windows Azure がクラウド環境であることから、Windows Azure でもこの懸念事項は当てはまるかどうかについて質問を受けたことがあります。良い質問ですね。確かに、Windows Azure システムでは個々のアプリケーションは互いに独立しています。そのため、一般的にはメモリ内にデータを公開してもそれほど問題になりません。クラウドのアプリケーションとメモリ領域を関連付けるのは非常に難しいでしょう。しかし、Windows Azure でも慎重な手法を採用し、使用後のデータを消去することをお勧めします。クラウドでもこうしたコードを常に実行しておかないと、他の脆弱性が今後明らかになることもあるでしょう。それほど問題ではなくても、この習慣を続け、この手法を実行し続けてください。
図 3 は、先ほど紹介したランダムな整数を生成する例を変更したものです。この例では、簡単なエラー処理を追加して、どのような場合でも必ず finally ブロックが実行されるようにしました。このブロック内では、バイト配列の値に対して非常に単純な繰り返し処理を実行して、配列内のすべての項目にゼロを上書きしています。バイト配列は可変なので、メモリ内のデータが上書きされます。したがって、このメンバーの実行時に所有されているメモリには、この数値が存在しなくなったことがわかります。この手法は、キー、初期化ベクトル、暗号化したデータ、暗号解除したデータなどの項目用のデータ バッファーとして使用されるすべてのバイト配列に使用できます。
図 3 メモリ内のデータの消去
public static int GenerateRandomNumber() {
byte[] GeneratedBytes = null;
try {
GeneratedBytes = new byte[4];
RNGCryptoServiceProvider CSP =
new RNGCryptoServiceProvider();
CSP.GetBytes(GeneratedBytes);
return BitConverter.ToInt32(GeneratedBytes, 0);
}
finally {
for (int x = 0; x < GeneratedBytes.Length; x++) {
GeneratedBytes[x] = 0;
}
}
}
メッセージ キュー
Windows Azure のキューでは、エンタープライズ Windows アプリケーションでよく使用されている Microsoft Message Queuing (MSMQ) サービスに似た機能のセットが提供されます。Windows Azure 内では、メッセージ キュー サービスは、先入れ先出し (FIFO) 方式で 8 KB までのテキスト ベースのメッセージを格納します。このため、サービスやアプリケーションが異なるサーバー上 (この場合はクラウド内) で実行されていても、セキュリティが確保された分散方式で、相互に対話し、実用的なメッセージを送信できます。
キューへのメッセージの書き込み、確認、取り出しなどを実行できる基本関数は 5 つあります。最も頻繁に出る疑問は、このようなメッセージはどの程度安全かということです。
現在 MSMQ でサポートされている機能の多くは、Windows Azure のメッセージング API ではまだサポートされていません。しかし、類似機能は存在します。ブロブ データ サービスと同様、メッセージング サービスでも同じ REST の get インターフェイスと put インターフェイスが使用されます。メッセージの書き込みと読み取りは、コードから、または URI と Web 要求呼び出しを使用して実行でき、セキュリティが確保されていないネットワーク経由であれば、この要求を SSL で暗号化できます。つまり、要求の送信は暗号化されます。
また、Windows Azure の他の Storage サービスと同様、1 つのメッセージ キューにアクセスするときは、すべて同じ Storage サービス キーを使用しなければなりません。このようなキューのメッセージを表示したり、メッセージを追加したりできるのは、キーにアクセスできるアプリケーションだけです。このため、これらのメッセージの本文がセキュリティ保護されたネットワークやセキュリティ保護されたアプリケーション領域の外部に送信されない限り、これらのメッセージを暗号化するのは過剰な処理です。
まとめ
サービス指向アーキテクチャやソリューションへと向かう現在の流れの中では、クラウド アプリケーションなしで業務を行うことは考えられなくなってきています。Windows Azure などのマルチテナント環境におけるデータとサービスの分離は、個人データの使用に目を向けているすべての人にとって重要な課題の 1 つです。
すべての新しいプラットフォームと同様、セキュリティと暗号化の機能は Windows Azure プラットフォームでも進化し続けるでしょう。マイクロソフトでは、安全で分離された環境を提供することだけでなく、こうした手段が広く認められるような活動を公開することにも積極的に注力してきました。このため、セキュリティ、およびシステムやアプリケーションのロックダウンの維持に関して、マイクロソフトがより密接なパートナーになることを望んでいると、エンジニアは確信できるでしょう。
セキュリティ、特に暗号化で最も重要な点は、情報やプロセスへのアクセスを非常に難しくすることです。"難しい" とは、データやプロセスが有効な間にシステムに侵入しようとする、あらゆる攻撃者の能力を上回ることだと言えます。しかし、これは使用するアプリケーションやデータの要件に基づいた相対的な定義でしかありません。この記事で、私がセキュリティと暗号化の要件を絶えず評価する必要性を強調し続けてきたのはこのためです。これらのツールを効果的に活用して、クラウド システムのセキュリティを確保し、データを保護できるようにするうえでは、こうした対策が不可欠です。
Jonathan Wiggs は、現在 Nuance Communications Inc. のプリンシパル エンジニアおよび開発マネージャーです。Wiggs のブログは、jonwiggs.com (英語) で公開されています。また、直接の連絡先は Jon_Wiggs@yahoo.com (英語のみ) です。