次の方法で共有


第10章 安全なWebサービスの構築

Webサービスの多くは、その成り立ちゆえに、インターネットという最も敵意に満ちた環境に置かれます。したがって、Webサービスに適切なセキュリティテクノロジを適用する必要があります。アプリケーションのどの部分が最も危険で、脅威を緩和するためにどのようなツールやテクニックを利用すべきかを知るために、「脅威モデル化」と呼ばれる手法を利用できます。

この章では、脅威モデル化について詳しく説明し、脅威モデルを利用して安全なWebサービスを構築する方法を示します。次に、IIS 5および6が提供するセキュリティテクノロジについて説明し、XML Signatures(XML署名)やXML Encryption(XML暗号化)など、XMLをベースとする重要なセキュリティテクノロジについても説明します。また、.NET Frameworkにおいてこれらのセキュリティテクノロジがどのようにサポートされているのかについても説明します。最後に、Webサービスを構築する際に陥りがちなセキュリティ上の間違いについて触れ、Webサービスを危険にさらすこれらの間違いを回避する方法を紹介します。

10.1 | 脅威モデルとは

安全なシステムの構築方法の要点を早く知りたいと思っていることでしょう。しかし残念ながら、最初に設計段階の話をしなければなりません。これは、ソフトウェアの設計が無秩序だと、セキュリティがその混乱の犠牲になることがよくあるからです。設計段階で一定の構造を提示する方法の1つが、「脅威モデル」の作成です。

脅威モデル化の根本となる原則は、「アプリケーションに対する脅威を把握しないかぎり、安全なシステムは構築できない」というものです。さいわい、脅威モデル化はシンプルで、楽しめるものです。そのうえ、設計仕様のセキュリティに関係する部分の基礎にもなります!筆者の経験では、脅威モデルを利用してサービスを構築した方が、より適切に設計されたセキュリティを持ち、より安全なシステムとなります。というわけで、しばらくお付き合いください。読む価値は十分にあります。

脅威モデル化には十分な時間をかけることが重要です。これは、コードを書き始める前に、設計段階でセキュリティ設計上のバグを見つけ、それらを修正する方が結果的に時間と手間が省けるからです。

脅威モデル化の過程は、大まかに次の3つの段階を踏みます。

  1. 脅威に関するブレインストーミングを行う。
  2. 脅威を緩和するためのテクニックを選ぶ。
  3. テクニックを適用するための適切なテクノロジを選ぶ。

以上の各段階を順に見てみましょう。

10.1.1| 脅威に関するブレインストーミング

開発チームで2~3時間かけてブレインストーミング会議を行い、潜在的な脆弱性を持つ領域について検討します。ミーティングでは、提案されたアーキテクチャをホワイトボードに図面として描いてもらい、次に示すようなWebサービスの重要な要素がすべて網羅されるようにします。

  • データストレージテクノロジ(ファイルストレージ、SQLデータベース、XMLファイル、およびレジストリ)
  • プロセス間通信テクニック(RPC、.NETリモート処理、ソケットなど)
  • ユーザー入力テクニック(SOAP引数およびHTTPメッセージ)
  • 非永続データ(回線上のデータなど)

次に、個々のコアコンポーネントがどのような危険にさらされるかを検討します。ここで紹介するのは、STRIDE脅威モデルというテクニックを使用する方法です。

STRIDE脅威モデルの導入

システムを構築する前に、次のような問いへの答えを用意しておくと便利です。

  • 攻撃者はオンラインショッピングの買い物かごをどのようにすれば乗っ取ることができるか。
  • 攻撃者によって、サービスへの正規ユーザーのアクセスが拒否されるようなった場合、どのような影響が出るか。
  • Webサービスから顧客に送られるデータを、どのようにすれば攻撃者が盗み見たり変更できるのか。

これらすべての重要な問いを、抜けのないようにたずねる方法として、脅威カテゴリを使用する方法があります。ここでは「STRIDE」という脅威モデルを使用します。STRIDEとは、次の6つの脅威カテゴリに由来する頭字語です。

  • なりすまし(Spoofing identity)
    不正なアクセスなどをして、別のユーザーの認証情報(ユーザー名とパスワードなど)を使用して身元を偽装することを意味する。
  • データの改ざん(Tampering with data)
    データの改ざんには、悪意のあるデータ変更などがある。例として、データベースなどに格納されている永続データに対して許可なく変更を加えたり、インターネットなどのオープンなネットワークを経由して2台のコンピュータ間で行き来するデータを変更したりする行為がある。
  • 否認(Repudiation)
    否認は、あるユーザーが特定の行為を実施したことを否定し、他にだれも実際にはその行為が行われたことを証明する手段がない状態を言う。たとえば、禁止されている操作を追跡する能力を持たないシステムで、ユーザーが不正な操作を実行する場合などがある。否認防止は、否認の脅威に対抗するための機能である。たとえば、ユーザーが商品を購入すると、商品の受け取り時に署名を求めるという方法が考えられる。ベンダはこの署名済みの受領書を、ユーザーが商品を受け取った証拠として使用できる。想像どおり、電子商取引では否認防止の機能はきわめて重要である。「私は注文していない!」などといったクレームを考えるとわかりやすいだろう。
  • 情報漏洩(Information disclosure)
    情報漏洩の脅威は、適切なアクセス権を持っていない人物に情報が漏洩することを言う。たとえば、アクセスを許可されていないはずのユーザーがファイルを読み取れたり、あるいは侵入者が2台のコンピュータの間を行き来するデータを読み取れたりする場合などがある。
  • サービス拒否(Denial of service)
    サービス拒否(DoS)攻撃は、たとえばWebサーバーを一時的に使用不可または使用停止の状態にするなどして、正当なユーザーに対するサービスを拒否することを言う。システムの可用性と信頼性を向上させるために、いくつかの種類のDoSの脅威に対して必ず防護策を設ける必要がある。
  • 権限の不正取得(Elevation of privilege)
    この種の脅威では、権限のないユーザーが権限を持ったアクセス権を取得することによって、システム全体を危険にさらしたり破壊したりするのに十分な権限を獲得する。権限の不正取得の脅威では、攻撃者がシステムのあらゆる防御を突破し、システム自身の信頼された一部となるという、実に危険な状況に陥る。

先に示した3つの問いを振り返ると、1番目はデータの改ざんの脅威(T)に関するものであり、2番目はDoSの脅威(D)に関するものであり、3番目は情報漏洩と改ざんの脅威(IとT)に関するものであることがわかります。

STRIDEモデルを自分のアプリケーションに適用するためには、個々の脅威について、それぞれが各ソリューションコンポーネントに与える影響、およびソリューションコンポーネントどうしのつながりや関係に与える影響を検討するのが最も簡単です。一言で言えば、アプリケーションの個々の要素に着目し、それらの構成要素や処理に対して何らかのS、T、R、I、D、またはEの脅威が存在するかどうかを調べます。大半の構成要素は数多くの脅威を抱えています。必ずそれらをすべて記録しておきます。

10.1.2| 脅威を緩和するためのテクニックの選択

次に、適切な緩和テクニックを選びます。表10-1に、STRIDEカテゴリに属する脅威を緩和するためのテクニックやテクノロジをいくつか示します。

▼表10-1 脅威を緩和するためのテクニック

脅威の種類 緩和のテクニック
なりすまし 基本認証、ダイジェスト認証、NTLM認証、Kerberos認証、X.509証明書、.NET Passport認証、フォーム認証などのテクノロジを使用してプリンシパルを認証する。クライアントの認証を必要とする場合と、サーバーの認証を必要とする場合とがある。
XMLDSIG(XML Digital Signatures)やPKCS #7など、デジタル署名の署名とその確認を行うことで、データがプリンシパルから送られたものであることを証明できる。XMLDSIGとPKCS #7についてはこの章でこの後説明する。
機密データを安全でない状態で保管しないこと。特に、パスワードやPIN(Personal Identification Number)などの認証データについては注意する。
データの改ざん 適切なアクセス制御リスト(ACL)やアクセス許可を使用してデータを保護する。ハッシュまたはメッセージ認証コードを使用して、データが改ざんされていないか確かめる。回線上のデータはSSL(Secure Socket Layer)/TLS(Transport Layer Security)またはIPSec(Internet Protocol Security)を使用して保護する。
否認 否認防止の手段としては、堅牢な認証や署名済みデータのほか、広範囲で安全なログ記録や監査などがある。
情報漏洩 適切なACLやアクセス許可を使用すれば盗み見からデータを保護できる。また、そもそもデータを格納しなければ、データが漏洩することはない。データの暗号化と復号化に使用するキーを漏洩の脅威から守ることができれば、暗号化法などのプライバシー保護テクニックも役立つ。SSL/TLSとIPSecは回線上の秘匿性を実現し、暗号化ファイルシステム(EFS)はファイルやフォルダのプライバシー保護を実現する。
サービス拒否 DoSの脅威に対する防御は難しい。これは、負荷の高いサーバーが、単純に処理が忙しいだけなのか、あるいは攻撃を受けているのかを判断するのが難しいからだ。ユーザー要求を制限すれば、有効なユーザーからのアクセスを締め出してしまうかもしれない。簡単な防御手段としては、非認証ユーザーが実行可能な操作を制限するなどの方法がある。たとえば、リソースの割り当てを匿名ユーザーには10%、正規ユーザーには90%などとする(リソースには、キャッシュデータ、CPU時間、ディスク容量、ネットワーク帯域幅、データベースへの接続などがある)。
以上に示したソリューションの一部はアプリケーションの中から直接制御できないものもある。これには、ファイアウォールやパケットフィルタリングルーターなどがある。
権限の不正取得 不要な権限やアクセス許可を要求しない。そうすることで、仮にコードにセキュリティ上の問題があり、攻撃者が悪意のあるコードを実行したり安全でない動作を引き起こすことができるとしても、権限が制限されているために攻撃者は大きな損害を与えることはできない

Webサービスの例

ここまで述べた事柄についてよくわからなくても、心配することはありません。例を通して理解していきましょう。図10-1に示した簡単なシナリオでは、WebサービスクライアントがWebサービスと通信します。そのWebサービスはさらにデータベースと通信してクライアントにコンテンツを返します。この例では、クライアントはピアプロセスではなく、ユーザーであると仮定しています。

図10-1 Webサービスのシナリオの例

▲図10-1 Webサービスのシナリオの例

ここに示した一般的なシナリオは多くのWebサービスに当てはまります。表10-2は、システムに対するいくつかの脅威とそれらの緩和方法をまとめたものです。

▼表10-2 Webサービスへの脅威と適切な緩和テクニック

標的 ID 脅威の種類 説明 緩和テクニック
Webサービス 1 S 攻撃者は分散DoS攻撃を使用してWebサービスを停止させ、自分で作成した悪意のあるWebサービスをインターネット上に配置する。クライアントアプリケーションは、悪意のあるサービスと通信していることに気付かない。 クライアント上で起動したSSL/TLS接続を使用してサーバーを認証する。
クライアントとWebサービスの間の回線上データ 2 TおよびI 攻撃者はクライアントからサーバーに送られるデータ、またはサーバーからクライアントに送られるデータを、盗み見または変更する。 SSL/TLSまたはIPSecを使用し、Webを行き来するデータを暗号化する。
クライアントとWebサービスの間の回線上データ 3 E 攻撃者はクライアントからサーバーに送られるパスワードを盗み見る。ユーザーが管理者の場合、攻撃者がそのユーザー名とパスワードを使用することが考えられる。 パスワードが平文のまま回線上を転送されることのない認証機構を使用するか、SSL/TLSまたはIPSecを使用して経路を保護する。
Webサービス 4 D 攻撃者が偽の要求を多数発行して、Webサービスの負荷を高め、処理速度を低下させたりする。 ファイアウォールを使用し、受信するデータを制限する。1人のユーザーまたは1つのIPアドレスから送信可能なデータ量を制限するロジックを作成する。ただし、IPアドレスを限定すると問題が起きることがある。正規ユーザーの多くは限られた数のIPアドレスしか持たないISPを使用しているため、実際にはプロキシサーバーの背後にいる複数のユーザーから要求が送られて来ているにもかかわらず、同じIPアドレスから無数の要求が送られて来ているようにみえる場合がある。
SQL Server データ 5 TおよびI 攻撃者がWebサービスを経由せずに直接SQL Server内のデータにアクセスする。 SQLクエリの結果内にアクセス可能な範囲を制限する。
SQL Server 6 S、T、R、I、D、およびE 攻撃者がSQL Serverに組み込まれているxp_cmdshell拡張ストアドプロシージャを使用し、悪意のあるコードをSQL Serverデータベースで呼び出す。このストアドプロシージャはサーバーで任意のコマンドを呼び出すことができる。 SQLクエリの結果内にアクセス可能な範囲を制限する。xp_cmdshellなど使用しない拡張ストアドプロシージャを削除する。SQL Serverに対してシステム管理者(sysadmin)アカウント(sa)として接続しない。このアカウントでは、xm_cmdshell拡張ストアドプロシージャの呼び出しなどSQL Serverの任意の作業を実行できるからである。

以上のシナリオは、安全なソリューション構築のためにどのようなテクニックやテクノロジを利用すべきかを決める際に、その過程を理解するうえできっと役に立ちます。アプリケーションに何かセキュリティのための「魔法の呪文」をかけて、アプリケーションが攻撃に対して安全であることを祈るよりも、ここで説明した対策を取り入れるほうがはるかに得策です。

さて、ここで話題を変えて、開発者がWebサービスをWindowsプラットフォーム上に構築する際に利用できる、セキュリティテクノロジについて説明することにしましょう。

メモ 脅威の分析と安全なシステムの構築については、『プログラマのためのセキュリティ対策テクニック』(Michael Howard、David LeBlanc共著、日経BPソフトプレス刊、2002年、原著は『Writing Secure Code』、Microsoft Press刊、2001年)を参照してください。

10.2 | Webサービスのセキュリティテクノロジ

脅威モデル化における第3の段階は、適切なテクノロジを選択し、選択した脅威緩和テクニックを適用することです。具体的なテクノロジについて説明する前に、現在のWebサービスインフラストラクチャがセキュリティ機能として何を提供しているのかを見てみましょう。

本書の執筆時点では、Webサービスが主に使用する通信プロトコルであるSOAPでは、セキュリティプロトコルは規定されていません。SOAPはこれらのサービスを提供するために、Webサーバーに依存し、場合によってはクライアントアプリケーションにも依存します。この主な理由は、SOAPが特定のトランスポートに依存しないためです。Webサービスは主にHTTPをトランスポートとして使用しますが、他のSOAPベースのサービスはSMTPその他のテクノロジをトランスポートとして使用することがあります。脅威モデルに基づいて、クライアントからすべてのバックエンドサーバーに送られる途中のデータの安全を必ず確保しなければならないと判断した場合には、このことが問題になる可能性があります。

一例として図10-2を見てみましょう。この図では、クライアントはWebサービスと通信します。Webサービスは、クライアントとWebサービスの間を移動するクライアントデータをSSLまたはTLSを使用して保護するものとします。そして、クライアントデータをバックエンドサービスに送ります。その際、ソケットをトランスポートとして使用します。

図10-2 クライアントとサーバー間のデータを保護するためにSSLまたはTLSを使用する

▲図10-2 クライアントとサーバー間のデータを保護するためにSSLまたはTLSを使用する

どこに問題があるかわかるでしょうか。SSL/TLSによって提供される保護は、クライアントとサーバーとの間のリンクにだけ適用され、サーバーとバックエンドアプリケーションサーバーとの間のリンクには適用されません。多くの場合、このことが問題になることはまずありません。しかし、脅威モデルにおいて、Webサービスとバックエンドサーバーの間で情報漏洩の脅威が存在すると判断した場合には、SSL/TLSではうまくいきません。たとえWebサービスとアプリケーションサーバーとの間でSSL/TLSを使用してデータを保護したとしても、Webサービス上ではわずかな時間ですがデータが平文になるため、サーバーのユーザーによって参照される可能性があります(もちろんWebサービスを管理している人を信用すべきではありますが!)。繰り返しますが、このリスクを承知で運用することも可能ですが、決定を下すときは脅威モデルだけを基準とすべきです。

さて、現在のWebサービスインフラストラクチャが提供するセキュリティ機能の基本的な理解を得たところで、セキュリティテクノロジを適切に適用する方法をいくつか見ていきましょう。

10.2.1| Webサービスの認証

認証とは、ユーザーやコンピュータなどの「エンティティ」(実体)が本当に表明しているとおりの人物またはものであることを証明する能力のことを言います。表明の内容は、エンティティ(「プリンシパル」とも呼ばれます)に資格情報を持たせることによって検証できます。資格情報はユーザー名とパスワードという形が一般的です。認証プロトコルは、一般に他のプロトコルよりも安全です。そうしたプロトコルでは、資格情報が本当に正しいユーザーから届いたものであって攻撃者によって再現されたものではないことを、より確実に保証できます。IIS上で実行されるWebサービスはいくつかの認証プロトコルを利用できますが、中でもよく使われるものを以下に示します。

  • 匿名認証
  • 基本認証
  • ダイジェスト認証
  • Windows認証
  • 証明書認証
  • フォーム認証
  • .NET Passport認証

以下に、それぞれを詳しく説明します。

匿名認証

匿名認証はその名のとおりのものです。つまり、認証は行われません。公開データに対して使用する認証機構です。認証を行わないので、SOAPクライアントを更新したりコードを追加したりする必要はありません。

基本認証

基本認証はその単純なしくみのために、おそらくインターネット上で最も一般的に使われている認証形式でしょう。基本認証はすべてのプラットフォームのすべてのブラウザで提供されています。しかし、要求のたびにクライアントからサーバーに送られるユーザー名とパスワードは平文のままで暗号化されないため、安全ではありません。したがって、チャネルをなんらかの方法で暗号化してユーザー名とパスワードを漏洩や悪意のあるユーザーから守る必要があります。無論、ユーザー名とパスワードが漏洩してもかまわないというのであれば、チャネルを暗号化する必要はありません。率直に言って、株式情報やニュースのヘッドラインといった機密でないデータをWebサービスで扱う場合にはこれで十分かもしれません。認証をデータへのアクセスを保護するための手段として使用することと、認証をユーザーの識別手段として使用し、ユーザーが要求した個人向けにカスタマイズされたコンテンツを提供することは違います。ここでも、アプリケーションにとって基本認証で十分かどうかを判断するために、脅威モデルを使用するべきです。

IIS 5および6では、基本認証に使用するアカウントは有効なWindowsアカウントである必要があります。ただし、基本認証をASP.NETで使用する場合、またはASP.NETを使用して記述したWebサービスで使用する場合は、データベースの検索を行って資格情報が正しいかどうかを判断できます。基本認証はIISの管理ツールを使って設定できます。

基本認証、ダイジェスト認証、またはWindows認証を使用する場合、SOAPクライアントの中で、次のようなVBScriptコードによってユーザー名とパスワードを設定できます。

                  
Set sc = CreateObject("MSSoap.SoapClient")
sc.mssoapinit("http://www.fabrikam.com/webservice/service.asmx?wsdl")
sc.ConnectorProperty("AuthName") = "username"
sc.ConnectorProperty("AuthPassword") = "password"
Status = sc.GetShippingStatus("10001")

C++とATLのSOAPクラスを使用してクライアント側アプリケーションを記述した場合は、CAtlHttpClient::AddAuthObjメソッドの中でユーザー名とパスワードを設定できます。クライアントコードをC#またはVisual Basic .NETなどの他のマネージ言語で記述した場合は、次のようなコードを使用してWebサービスへの認証接続を作成できます(このコードは基本認証、ダイジェスト認証、およびWindows認証で動作します)。

                  
using System;
using System.Net;
using System.Web.Services.Protocols;

ClientApp.localhost.Service s = new ClientApp.localhost.Service();
s.Credentials = new NetworkCredential(username, password, domain);
string shipped = s.GetShippingStatus("10001");

全体の中で考えた場合、基本認証は、特にSSL/TLSを利用しないときは、きわめて脆弱です。

ダイジェスト認証

ダイジェスト認証は基本認証と同様、インターネットの標準となっています。どちらもRFC 2617(http://www.ietf.org/rfc/rfc2617.txt)で規定されています。しかし基本認証とは異なり、ダイジェスト認証はサポートをしているブラウザがInternet Explorer 5以降だけであるため、一般的ではありません。ダイジェスト認証ではユーザーのパスワードが平文で転送されることはなく、パスワードとサーバーが提供するデータのハッシュ(ダイジェスト)を使用してユーザーを認証します。ダイジェスト認証は、基本認証よりは多少安全なのは確かであり、SSLを使用してユーザーのパスワードを隠す必要がないという利点もあります。ただし、先に述べたように「多少」安全であるにすぎません。また、ダイジェスト認証はActive Directoryがインストールされている場合にだけ、IIS 5および6で動作します。これは、Active Directoryにおいて、ユーザーのパスワードのコピーを、暗号化したプレーンテキストとして格納するように設定できるためです。ダイジェスト認証ではこの機能が必要になります。

基本認証と同じように、IISの管理ツールを使うと、ダイジェスト認証を要求するようにWebサーバーを設定できます。

Windows認証

Windows認証は、その名が示すようにWindowsに組み込まれている認証機構を使用します。Windows 2000以降では、この認証はNTLMおよびKerberos認証のことを指します。Kerberos認証はActive Directoryが導入されているときだけ有効です。Windows認証の最大の欠点はインターネット経由で適切に動作しないことですが、ユーザーのログオン情報を直接使用できる、ユーザー名とパスワードの入力を必ずしも要求しない、パスワードが回線上を平文のまま行き来することが決してない、などの理由から、イントラネットのソリューションとしては非常に優れています。

Windows認証も他のプロトコルと同様に設定できますが、ASP.NET Webサービスの場合は次のコードをweb.configファイルに追加することでWindows認証を選択できます。

                  
<authentication mode = "Windows">
</authentication>

web.configの次の設定エントリを使用し、ASP.NETで呼び出し側ユーザーを偽装することもできます。

                  
<system.web>
  <identity impersonate="true" />
</system.web>

そして、Webサービスの中で次のコードを使用し、呼び出し側ユーザーの名前を取得できます。

                  
using System.Security.Principal;
WindowsIdentity wi = WindowsIdentity.GetCurrent();
string name = wi.Name;

証明書認証

証明書では、パスワードではなく大きな数値の秘密キーを使用してプリンシパルの身元を判断します。証明書の使い方と使う理由を詳しく説明する前に、少し寄り道をして証明書のしくみを支えるテクノロジについて簡単に見てみましょう。データの暗号化と復号化を1つのキーで行う対称暗号化方式(秘密キー暗号化方式)とは異なり、証明書では公開キー暗号化方式とも呼ばれる非対称暗号化方式を使用します。証明書が作成されると、秘密キーと公開キーという2つの大きな数値から成るキーが作成されます。秘密キーは、その名前が示すように非公開であり、保護すべきキーです。公開キーは公開することができ、証明書に埋め込まれます。証明書には秘密キーの所有者に関する情報が収められます。

公開キー暗号化方式には次の重要な特性があります。

  • 公開キーで暗号化されたデータは秘密キーによってのみ復号化できる。
  • 秘密キーで暗号化されたデータは公開キーによってのみ復号化できる。

秘密キーを持っている送信者は、認証されたメッセージを他者に送信できます。つまり、受信者は、データを送信したのが確かに送信者本人であることを確認できます。なぜなら、送信者は秘密キーを使用して暗号化しますが、そのキーは「秘密」であり、送信者だけが持っているからです。そして、秘密キーで暗号化されたメッセージを復号化できるのは対応する公開キーだけです。公開キーは送信者の証明書の中に収められており、証明書には送信者に関する詳細情報が含まれているので、データを送信したのは確かに送信者であると断定できます。

秘密キーを使用して暗号化することを「署名」とも言います。ある人物の公開キーを持っている人は、暗号化されたメッセージをその人に送信できます。署名の手順は実際にはもう少し複雑で、処理速度上の理由から、データそのものに対してではなくデータのハッシュに対して署名を行います。

今日の証明書のほとんどはRSAと呼ばれる暗号化アルゴリズムを使用しています。RSAは米国の特許でしたが、2000年9月にその有効期限が切れました。

メモ RSAアルゴリズムではキーの作成時に大きな素数を使用します。期限切れとなったRSAの特許番号は4,405,829です。この番号はなんと素数です!

SSL/TLSを使用すると、対称暗号化処理によって、クライアントとサーバーの間のセッションが盗み見から保護されます。そして、サーバーもまた認証されます。Barnes & Nobleのオンライン書店(http://www.bn.com/)で本を買うために決済処理を開始するときにはSSL/TLSセッションが確立されます。セッション確立時のハンドシェイクのときに、参照対象のWebサイトの名前と証明書内の名前が比較され、証明書から取得したサイトの公開キーを使って暗号化されたクライアントの任意のデータを、サーバーが正しく復号化できるかどうかが調べられます。サイトが暗号化されたデータを復号化できれば、サイトには証明書の公開キーに対応する保護された秘密キーがあると判断できます。また、証明書の有効期限も確認されます。以上の手順がすべて正常に行われると、サーバーが認証され、偽のサイトではなく本物のBarnes & Noble Webサイトと通信していることがわかります。

SSL/TLSに関しては、クライアント認証処理もオプションでサポートしているという利点があります。この場合、サーバーは証明書をクライアントに要求し、渡された証明書の公開キーを使用して暗号化されたデータをクライアントに復号化させます。さらに、成功するものと期待されるいくつかのチェックが行われた後、クライアントも認証されます。SSL/TLSによるクライアント認証はSSL/TLSの接続処理におけるオプション手順であり、非常に高いセキュリティが要求される環境を除けば、実際に使用されることはめったにありません。きわめて高度なセキュリティが必要な環境では、証明書と秘密キーをスマートカードに保管できます。

SOAP Toolkitの使用時に次のコードを使用すると、SOAPクライアントに特定のクライアント証明書を強制的に使用するよう指示できます。

                  
sc.ConnectorProperty("SSLClientCertificateName") = "mike@fabrikam.com"

ここでは証明書のサブジェクト名の一般名(CN)の部分を指定します。同じサブジェクト一般名を持つ証明書が複数ある場合は、最初の証明書が選択されます。

クライアントコードをVisual Studio .NETのWeb参照プロキシコード(Web参照を作成すると自動生成される)を使用して記述している場合は、サービスのClient

Certificateコレクションを使用してクライアント証明書を設定できます。現時点では、SOAP Toolkitのコードと同じようにCryptoAPI(CAPI:暗号化API)ストア内の証明書にアクセスすることはできません。証明書ファイルを使用する必要があります。

フォーム認証

フォーム認証はユーザー認証のための業界標準ではありませんが、非常に一般的な認証方法です。通常、フォーム認証とは、未認証の要求がHTTPリダイレクションによってHTMLフォームにリダイレクトされるシステムのことを指します。ユーザーは資格情報(ユーザー名とパスワード)を入力してフォームを送信します。アプリケーションがデータベースまたはXMLファイルを参照して要求が認証されると、ユーザーはアクセスを許可されます。

フォーム認証ではHTMLページを使用するため、Webサービスではサポートしていません。これは、Webサービスがユーザーインターフェイスを持たないからです。とはいえ、ASP.NETはフォーム認証をサポートしています。

.NET Passport認証

Microsoftが提供している.NET Passportは、シングルサインオンおよびプロファイルのサービスをメンバーサイトに提供する集中管理型の認証サービスです。この認証方式には、保護されたリソースやサイトに新しくアクセスするたびにログオンする必要はない、という利点があります。自分のサイトを.NET Passportによる認証と承認に対応させたい場合は、この.NET Passportが使用すべき認証プロバイダになります。詳細は.NET Passportサイト(https://www.microsoft.com/japan/myservices/passport/)を参照してください。

.NET Passport認証ではCookieを使用するため、現時点ではWebサービスではサポートされていませんが、将来的にははサポートされる予定です。ASP.NETはIIS 6と同様に.NET Passport認証をサポートしています。

IIS 5の認証技術に関する詳細な説明については、『Designing Secure Web-Based Applications for Microsoft Windows 2000』(Michael Howard著、Microsoft Press刊、2000年)を参照してください。

10.2.2| Webサービスの承認

アプリケーションで認証機構を使用してプリンシパルを識別したら、次に、サービスによって保護されているさまざまなリソースに対してユーザーがアクセス権を持ち、特定の機能を呼び出すことができるかどうか判断する必要があります。Windows 2000以降ではリソースへのアクセスを許可するための手段が数多く提供されていますが、最も広く使われていて重要なのがACLです。

Windows NT以降では、随意アクセス制御を利用することによって、保護可能なリソース(ファイルなど)を不正アクセスから保護します。随意アクセス制御は随意アクセス制御リスト(DACL。通常はACLと略記します)を通じて実装されています。ACLは一連のアクセス制御エントリ(ACE)で構成されています。ACEにはそれぞれプリンシパルが列挙されており、プリンシパルに関する情報と、プリンシパルがリソースに対して実行できる操作の情報が収められています。たとえば、ある人物には読み取りアクセスを許可し、別の人物にはフルコントロールを許可する、というようにできます。

ACLは、適切な暗号化とキーの管理を除けば、実質的に攻撃に対するアプリケーションの最後の砦になります。攻撃者がリソースにアクセスすることができたら、攻撃者の仕事は済んでしまいます。したがって、保護対象のリソースが適切なACLを持っていることがきわめて重要になります。たとえば、すべてのユーザー(Everyoneとも呼ばれます)に対して機密ファイルへのフルコントロールアクセスを許可すべきでしょうか。おそらくすべきではありません。ただし、特定のユーザーには許可する必要はあるでしょう。

ACLが本当にすばらしい点は、アクセス経路に関係なく常に同じやり方が適用されることです。そのため、なんらかの方法で攻撃者がサービスを迂回することに成功し、それによってアプリケーションレベルの承認ロジックが回避されてリソースに直接アクセスできたとしても、リソースのACL内の承認ポリシーが依然として立ちふさがります。

承認は、ASP.NET内およびアプリケーション内、またはデータベース権限内部(データベースを使用している場合)において、アプリケーション層の少し上層に位置しています。ASP.NETでは各種の豊富な承認テクニックが提供されており、そうしたポリシーを適用するのはXMLプロパティの設定のように簡単です。たとえば、次のコードをweb.configファイルに追加することによって、Webサービスの特定部分に対する匿名ユーザーのアクセスを制限できます。

                  
<authorization>
  <deny users="?" />
</authorization>

このとき、「?」は匿名ユーザーを、「*」はすべてのユーザーをそれぞれ意味します。<allow users="name" />を使用して特定のユーザーにアクセスを許可することもできます。nameは、コンマ(,)で区切られた1つまたは複数の名前です。

10.2.3| Webサービスのプライバシーと整合性

SOAPデータのプライバシーと整合性について考えるときは、どちらのテクノロジにも2つの側面があることに注意してください。1つはクライアントとWebサービス間の通信のセキュリティ保護であり、もう1つはSOAPのペイロードまたはペイロード内部のデータのセキュリティ保護です。ペイロードまたはペイロード内部のデータについては、クライアントからWebサービス、あるいはWebサービスからクライアントに移動するときだけでなく、いずれかの永続データストレージに格納するデータについてもセキュリティ保護する必要があります。前者は十分理解されており、コードを変更しなくてもSSL/TLSやIPSecを使用することで容易に処理できます。これらのプロトコルはアプリケーション層(サービスが存在する層)よりも下にあるため、完全に透過的です。Webサーバーの簡単な設定を変えるだけで、接続がSSL/TLSで保護されます。

今のところ、SOAPメッセージを暗号化したりペイロードのデータの整合性を確保したりする標準的な方法はありません。特に、任意のクライアントによるサービスへの接続を許可したい場合には、標準的な手段をサポートする必要があります。これらの標準規格については現在設計が進められています。今後の動向については、「10.4 将来のWebサービスのセキュリティテクノロジ」を参照してください。

では、独自の暗号化のしくみや整合性を確保するための機構を導入したいときはどうすればよいのでしょう。クライアントコードとサーバーコードの両方を管理している場合には、データ形式を自分で決められるので、比較的簡単です。MSDNサイトにSOAP拡張を使った非常に簡単なコードの例があります(https://www.microsoft.com/japan/msdn/columns/asp/asp09272001.aspx)。ただし、本書の執筆時点では、このコードはハードコードされたキーを使用してDES(Data Encryption Standard)でデータの暗号化と復号化を行っています。このコードをSOAPアプリケーションの中で使用する場合は、キーをソースコードの中にではなく、レジストリ、あるいは保護された(ここでいう「保護」とはファイルがWebサーバーを通じて容易にアクセスできず、適切なACLを使用して保護されている、という意味)XML構成ファイルなど、コンピュータのどこか別のところに格納してください。独自の暗号化方式を使用してシステムを設計するとき、キーの管理は解決が非常に難しい問題となります。1つの対処方法としては、SSL/TLSを使用してサーバーからクライアントにキーを転送するという方法があります(クライアントにキーを決めさせてはいけません。攻撃者が脆弱なキーを選択するかもしれません!)。そして、通信はオープンに行い、適切なSOAPデータを暗号化します。

次のコードを使用すると、SOAPメソッドが、SSL/TLSを使用して守られているチャネルを経由して起動されたのかどうかを判断できます。

                  
if (HttpContext.Current.Request.IsSecureConnection) {
    // 接続でSSL/TLSが使用されている
} else {
    // 接続でSSL/TLSが使用されていない
}

このコードでは、クライアントとサーバー間のチャネルを保護するためにIPSecが使用されているかどうかは検出できません。実のところ、現時点ではマネージ環境からIPSecが使用されているかどうかを簡単に知る手段はありません。

キーの話に戻りますが、暗号化キーの作成に使用するような高品質の乱数を生成する必要がある場合は、Randomクラスは使用しないでください。Randomクラスはきわめて予測しやすい乱数を生成します。代わりに次のようなコードを使用します。このコードは32バイトの非常にランダムな値を生成します。

                  
using System.Security.Cryptography;
byte[] b = new byte[32];
new RNGCryptoServiceProvider().GetBytes(b);

10.3 | .NET Frameworkにおけるセキュリティテクノロジ

.NET Frameworkではデータの暗号化と署名のためのサポートが提供されていますが、特筆すべきなのは、任意のデータストリームの暗号化と署名、そしてXMLデータのための特別なサポートです。後者はWorld Wide Web Consortium(W3C)の標準規格であるXMLDSIGのサポートを通じて提供されています。

2つのホストの間でデータの暗号化と復号化に使用する共通のキーが決まれば、次のようなコードを使用するだけで暗号化と復号化が行えます。このコードではRC2対称暗号化方式を使用しています。

                  
static string Encrypt(string plaintext, byte [] key, byte [] IV) {
    try {
        MemoryStream ms = new MemoryStream();
        RC2 rc2 = new RC2CryptoServiceProvider();
        CryptoStream s = new CryptoStream(ms, 
            rc2.CreateEncryptor(key, IV), 
            CryptoStreamMode.Write);
        byte [] p = Encoding.UTF8.GetBytes(plaintext.ToCharArray());
        s.Write(p,0,p.Length);
        s.FlushFinalBlock();

        return Convert.ToBase64String(ms.ToArray());
    } catch(Exception) {
        return null;
    }
}

static string Decrypt(string ciphertext, byte [] key, byte [] IV) {
    try {
        MemoryStream ms = new MemoryStream();
        RC2 rc2 = new RC2CryptoServiceProvider();          
        CryptoStream s = new CryptoStream(ms, 
            rc2.CreateDecryptor(key, IV), 
            CryptoStreamMode.Write);
        byte [] c = Convert.FromBase64String(ciphertext);
        s.Write(c, 0, c.Length);
        s.FlushFinalBlock();

        return Encoding.UTF8.GetString(ms.GetBuffer());
    } catch(Exception) {
        return null;
    }
}

これで、Webメソッドのデータを単純に平文で回線上に送信するのではなく、データを暗号化してBase64エンコード文字列として渡したり、同じ方法でサーバーから機密データを送り返したりできます。次のSOAPメッセージのように、人の読める内容があるとします。

                  
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3c.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3c.org/2001/XMLSchema" 
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetMeetingResponse xmlns="http://www.fabrikam.com/soap">
      <GetMeetingResult>
        Meet at Midnight!
      </GetMeetingResult>
    </GetMeetingResponse>
  </soap:Body>
</soap:Envelope>
<p>暗号化により、次のような、より安全なメッセージにできます。</p>
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetMeetingResponse xmlns="http://www.fabrikam.com/soap">
      <GetMeetingResult>
        LslO+R09UUMziJdQ1Q4P0POzaFxqGHS=
      </GetMeetingResult>
    </GetMeetingResponse>
  </soap:Body>
</soap:Envelope>

これは、メソッドの終了時に暗号化関数を呼び出すだけで実現できます。たとえば、次のようなコードがあるとします。

                  
[WebMethod]
Public string GetMeeting() {
    ...
    return meetingdata;
}

これを次のようにします。

                  
[WebMethod]
Public string GetMeeting() {
    ...
    return Encrypt(meetingdata, key, IV);
}

ここでは、キーと初期化ベクタの値が双方で既に合意されているものとします。

初期化ベクタとは何か

初期化ベクタ(IV)は乱数であり、通常は暗号化アルゴリズムのブロックサイズと同じビット数を使います。IVはデータセットを暗号化する際の開始点として使用します。

IVを使用しない場合、2つのまったく同じ平文メッセージを同じキーで暗号化すると、生成される2つの暗号化メッセージはまったく同じになります。しかし、平文メッセージをそれぞれ異なるIVを使って暗号化すれば、生成される暗号化メッセージもまったく異なります。

セキュリティを向上させるため、メッセージはそれぞれ異なるIVで暗号化しましょう。特に、メッセージに重複する部分が大量に含まれているときはIVを使うことを強くお勧めします。アプリケーションは、暗号化されたメッセージと一緒にIVを送信する必要がありますが、IV自体を暗号化する必要はありません。

この方法で問題になるのは、どちらも独自のコードが必要になることです。クライアントとサービスの両方が管理下にある場合は問題ありませんが、任意のクライアントから自分のサービスに接続できるようにしたい場合には問題になります。そこで、近い将来実現されるWebサービスのセキュリティテクノロジを紹介することにします。

10.4 | 将来のWebサービスのセキュリティテクノロジ

2001年10月、MicrosoftはGlobal XML Web Services Architecture(GXA)を発表しました。GXAは今日のWebサービスプロトコルであるSOAP、WSDL、UDDIなどを基盤としており、WS-Security(Web Services Security Language)と呼ばれるセキュリティテクノロジなどの構成要素を追加します。

WS-SecurityはWebサービスのためのセキュリティ言語を提供します。WS-SecurityはSOAPのメッセージング機能を拡張して、資格情報の交換、メッセージの整合性、メッセージの機密性という、3つの機能を提供します。これら3つの機能は、広範囲な認証および暗号化のテクノロジに対応するように、個別に、あるいは組み合わせて使用できます。

仕様については、https://www.microsoft.com/japan/msdn/webservices/spec/ws-security.aspx を参照してください。

10.5 | よくあるセキュリティ上の間違い

攻撃に耐え得るWebサービスを構築するためには、セキュリティ機能をあちこちにばらまくだけではだめです。既に述べたように、セキュリティを念頭におきながらアプリケーションを設計する必要があります。

ここでは、開発者(Webサービス開発者を含む)が陥りやすいセキュリティ上の最も深刻な間違いを3つ紹介します。

  • 安全でない状態での機密データの格納
  • 誤った方法でのSQL Serverへの接続
  • 安全でないSQL文字列の作成

.NETの共通言語ランタイム(CLR)と.NET Frameworkは、バッファオーバーランなど、最近の多くのセキュリティ上の脅威を緩和するのに役立ちますが、設計上の不適切な決定に対処することはできません(実のところ、3つの間違いはすべて、あらゆるプログラミング環境およびオペレーティングシステムに当てはまります)。

では、個々の間違いについて詳しく見てみましょう。

10.5.1| 間違いその1: 安全でない状態での機密データの格納

機密データを安全に格納することはソフトウェアレベルでは不可能であり、攻撃者によるデータの取得をできるだけ困難にすることしかできません。データの安全な格納は、サーバー上で行う方が、攻撃者がソフトウェアに物理的にアクセスできないのでいくらか簡単です。しかし、機密データをクライアントコードに格納することはまったく不可能です。クライアントアプリケーションに埋め込まれた機密データに基づいて秘密裏にハンドシェイクを実行できる、あるいは秘密裏にトランザクションを実行できる、などと考えているとしたら、それは間違いです。攻撃者はクライアントアプリケーションのリバースエンジニアリングを行って機密データを簡単に調べることができるのです。

機密データには、パスワードや暗号化キー、ID番号、売上高、クレジットカード番号といった、非公開のデータや個人データがあります。データを格納するときは、万一データが悪意のあるユーザーに漏れてしまった場合(STRIDEモデルのI)、あるいは攻撃者によって改ざんされてしまった場合(STRIDEモデルのT)に、自分自身や自分の仕事、自分の顧客に対してどのようなことが起こり得るのかを十分考慮する必要があります(これらの問題がまだよく理解できないという方は、自分の個人データが格納されている場合を想像してみてください!)。

情報漏洩の脅威は対処しやすい問題です。単に、初めからデータを格納しなければよいのです。至極真面目な話です。場合によっては、ユーザーのデータを格納する際に、ユーザーに代わってデータを保管するという選択肢をユーザーに提示するのも有効です。ただし、ユーザーに意識して選択してもらうようにする必要があります。ユーザーに代わってデータを保管した場合、ユーザーは楽になる一方で、データが攻撃者に対して適切に保護されていなければ保管者を非難するかもしれません。

データによっては格納する必要がないものもあります。たとえば、ユーザーがそのデータを知っているかどうかを確認するためだけに使用するものがあります。その一例がパスワードです。パスワードはそれ自体を格納していなくても、ユーザーが知っているかどうかを調べることができます。そのためには、データをハッシュ処理してそのハッシュだけを格納します。そして、ユーザーがパスワードを入力したときに、コードの中でそのパスワードをハッシュ処理し、格納されているハッシュと比較します。2つのハッシュが同じであればユーザーはパスワードを知っていることになります。

次のサンプルコードは、ユーザーからパスワードを取得して別のハッシュと比較する方法を示すものです。

                  
public bool ComparePasswordHash(string password, byte[] hash) {
    SHA1Managed h = new SHA1Managed();
    UTF8Encoding e = new UTF8Encoding();
    byte[] p = e.GetBytes(password);

    h.ComputeHash(p);
    byte[] hr = h.Hash;

    bool same = true;
    for (int i =0; i < hr.Length; i++) {
        if (hr[i] != hash[i]) {
            same = false;
            break;
        }
    }

    return same;
}

しかし、パスワードなどの機密データを格納しなければならない場合はどうすればよいのでしょう。どこに格納し、どのようにして守ればよいのでしょう。データを格納できる場所の1つにweb.configファイルがあります。ただし、攻撃者が構成ファイルに直接アクセスできるとしたら、データは読み取られてしまいます。.aspxファイルや.asmxファイルにデータを格納しても同じことが言えます。配置の問題はさておき、機密性の高いデータをこれらのファイルに含めることは避け、Web領域の外に置くべきです。たとえば、Webファイルシステム領域のルートの外にあるファイルシステムにデータを格納したり、システムレジストリなどファイルシステム以外の場所に格納したりすることが考えられます。

10.5.2| 間違いその2:誤った方法でのSQL Serverへの接続

開発者の多くが、SQL Serverを含むSQLデータベースに、システム管理者アカウントを使用する誤った方法で接続しています。その理由はシステム管理者アカウントならばすべての作業が行えるのでテストが簡単にできるからですが、残念ながら、このことは攻撃者にとってもあらゆる操作が行える可能性があることを意味しています。

SQL Serverのシステム管理者アカウント、つまりsaアカウントは、SQL Serverで使用できるアカウントの中でも最も機能や権限の強いアカウントであり、SQL Serverデータベースに対してあらゆる操作を実行できます。Windows認証ではなく、SQL認証を使用しなければならない場合は、SQL Serverデータベースにおいて必要な権限だけを持ち、データベース内の不要なオブジェクトにはアクセスできないアカウントを使用して接続してください。

また、非常に推測しにくいパスワードをアカウントに持たせるようにすることも重要です。

10.5.3| 間違いその3:安全でないSQL文字列の作成

次のようなコードでSQL文字列を作成したことのある方も多いと思います。

                  
string sql = "select * from table where name = '" + name + "'";

変数nameの値はユーザーが指定します。このSQL文字列で問題なのは、攻撃者が変数nameの値に悪質なSQLステートメントを指定できてしまうことです。

たとえばname = "Blake"と入力したとします。この場合、まったく無害なSQLステートメントが作成されます。

                  
select * from table 
where name = 'Blake'

しかし、攻撃者が"Blake' delete from table where name = 'Lynne' --"と入力したとしたらどうなるでしょう。次のような悪意のあるステートメントになります。

                  
select * from table 
where name = 'Blake' 
delete from table where name = 'Lynne' --'

このステートメントはtabelテーブル内のBlakeという名前が含まれているすべてのデータを返し、それからLynneという名前が含まれているすべての行を削除してしまいます!これは序の口です。攻撃者の多くはもっと悪意のある操作を試みます。なぜこのようなことが起こるのでしょう。それは、システム管理者アカウントを使ってSQL接続しているからです。システム管理者アカウントは、データの削除を始め、あらゆる操作をデータベースに対して実行できます。'--'という部分に注目してください。これはコメント文字で、攻撃者はこの文字を使用してより簡単に悪事をこなせます。

では、次に1つの例といくつかの対処方法を紹介することにしましょう。

10.6 | 詳細な例

ここまで、安全なWebサービス構築のための最善の方法とよくある間違いについていくつか紹介してきたので、より詳細な例を見てみることにします。まず、安全でない一般的なシナリオを取り上げ、それからWebサービスを守るためのいくつかの手段を示すことにします。

10.6.1| 安全でないシナリオ

次のC#コードを見て、どこに脆弱性があるのかを考えてみてください。

                  
[WebMethod(Description="Dangerous Shipping Status")]
public string GetShippingStatus(string Id) {
    string Status = "No";
    string sqlstring ="";
    try {
        SqlConnection sql= new SqlConnection(
            @"data source=localhost;" + 
            "user id=sa;password=password;" + 
            "initial catalog=Shipping");
        sql.Open();

        sqlstring="SELECT HasShipped" +
            " FROM detail " +
            " WHERE ID='" + Id + "'";

        SqlCommand cmd = new SqlCommand(sqlstring,sql);
        if ((int)cmd.ExecuteScalar() != 0)
            Status = "Yes";

    } catch (SqlException se) {
        Status = sqlstring + " failedエnエr";
        foreach (SqlError e in se.Errors) {
            Status += e.Message + "エnエr";
        }
    } catch (Exception e) {
        Status = e.ToString();
    }

    return Status;
}

脆弱な部分を見つけることはできたでしょうか。それでは、答えです。最初の間違いは、saアカウントとしてSQL Serverデータベースに接続していることです。テーブルに対するクエリのためだけにこのような権限の大きいアカウントを使用する必要はありません。小さな間違いではありますが、saアカウントは何でもやりたい放題です。普通のデータテーブルはもちろん、SQL Serverのマスタテーブルも削除や変更が可能です。

次の間違いは、saアカウントに対して推測が容易なパスワードを持たせていることです。そして3つ目は、パスワードをWebサービスページに埋め込んでいることです。攻撃者がこのページにアクセスした場合、攻撃者は接続の詳細を知り、Webサービスを提供するコンピュータ上でSQL Serverが稼働していることを知ります。

おそらく最も危険な問題は、コードにSQLステートメントを入力できてしまうことです。攻撃者がIDに対して有効な値を設定し、その後に一連の危険なSQLステートメントを入力することで、それらをすべて実行できてしまいます。また、無効なSQLステートメントや接続の失敗など、なんらかの理由でSQL Serverとの通信が失敗した場合には、Webサービスから、SQLステートメントを構成する文字列を始め、大量のデータがクライアントに送り返されます。これだけでも攻撃者にとっては十分な情報になります。これらの情報は、実際には、開発者以外の人にとってはほとんど意味がありません。

さて、このコードにはもう1つ脆弱性があるのですが、見つけられたでしょうか。細かなことなので気付かなかったかもしれません。攻撃者がこのコードに文字列を送信したとしましょう。その文字列は無効なSQLステートメントを作成するものです。SQLクラスは例外をスローします。しかしSQL Serverへの接続は切断されません。最終的にはガベージコレクションによって解放されますが、攻撃者が無効な要求を無数に送ったとしたらどうでしょうか。SQL Serverへの接続が溢れて、有効な接続さえ失敗してしまいます。これは立派なDoS攻撃となるおそれがあります。

10.6.2| 安全なソリューション

今度は、安全なソリューションを紹介しましょう。ここでは複数の防御層を持たせ、ある防御機構が破られたとしても、少なくとも1つ以上の別の層がアプリケーションとデータを保護します。

                  
[SqlClientPermissionAttribute(SecurityAction.PermitOnly,
AllowBlankPassword=false)]
[RegistryPermissionAttribute(SecurityAction.PermitOnly,
Read=@"HKEY_LOCAL_MACHINEエSOFTWAREエShipping")]

public string SafeGetShippingStatus(string Id) {

    SqlCommand cmd = null;

    string Status = "No";
    try {
        // 有効な出荷IDかどうかをチェックする
        Regex r = new Regex(@"^エd{10}$");
        if (!r.Match(Id).Success)
            throw new Exception("Invalid ID");

        // 接続文字列をレジストリから取得する
        SqlConnection sqlConn= new SqlConnection(ConnectionString); 

        // 出荷IDパラメータを追加する
        string str="sp_HasShipped";
        cmd = new SqlCommand(str,sqlConn);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@ID",Id);

        cmd.Connection.Open();

        if ((int)cmd.ExecuteScalar() != 0)
            Status = "Yes";

    } catch (Exception e) {
        if (HttpContext.Current.Request.UserHostAddress == "127.0.0.1")
            Status = e.ToString();
        else
            Status = "Error.";
    } finally {
        // 接続を閉じる(失敗時であっても)
        if (cmd != null)
            cmd.Connection.Close();
    }

    return Status;
}


// 接続文字列を取得する
internal string ConnectionString {
    get {
        return (string)Registry
                    .LocalMachine
                    .OpenSubKey(@"SOFTWAREエShippingエ")
                    .GetValue("ConnectionString");
    }
}

一見このコードは複雑そうに思えますが、実際にはそうではありません。このコードがなぜ最初の例よりも安全なのかを説明します(関数呼び出しの前にある属性についてはこの節の最後に説明します)。

まず、このコードでは出荷ID番号を必ず10桁にしなければならないことを強制しています。このことは正規表現の^\d{10}$を使用して指定していますが、この正規表現は入力データの先頭(^)から末尾($)までのうち10桁の数字(\d{10})しか検索しません(正規表現は、System.Text.RegularExpressions名前空間を通じて公開されています)。何が有効な入力なのかを宣言し、その他の入力をすべて拒否することで、既に前よりも安全になっています。そのため、攻撃者は出荷IDの後にSQLステートメントを付けることはできません。

このコードにはさらに多くの防御機構があります。SqlConnectionオブジェクトがレジストリから取得した接続文字列に基づいて作成されています。また、アクセサ関数ConnectionStringを見てください。攻撃者が接続文字列を知るには、Webサービスのソースコードだけでなく、該当するレジストリキーにもアクセスできなければなりません。

レジストリキー内のデータは次のような接続文字列です。

                  
data source=db007a;
user id=shipuser;
password=&ugv4!26dfA-+8;
initial catalog=Shipping

SQL Serverデータベースが別のコンピュータ上にあることにも注目してください。これによって、Webサービスに攻撃を仕掛ける攻撃者は、SQL Serverデータに自動的にアクセスできません。また、このコードではsaアカウントで接続せず、代わりにshipuserという特定のアカウントと、推測の難しいパスワードを使用しています。そして、この特別なアカウントには適切なSQLオブジェクトに対する読み取りと実行のアクセス権だけを持たせています。Webサービスからデータベースへの接続が破られたとしても、攻撃者が実行できるのは少しばかりのストアドプロシージャと、アクセス可能なテーブルに対する照会だけです。もちろん、マスタデータベースは破壊できません。

この例で示したSQLステートメントは、安全でない文字列連結のテクニックは使用せず、パラメータ化されたクエリを使用してストアドプロシージャを呼び出しています。ストアドプロシージャを呼び出す方が、データベースやテーブルの名前が知られることがなく、ストアドプロシージャがデータベースエンジンによって最適化されるため、文字列連結よりも高速で安全です。

エラーが発生した場合も、要求がローカルか、またはWebサービスの置かれているのと同じコンピュータで生成されたものでないかぎり、ユーザー(または攻撃者)には何も知らされません。Webサービスを実行しているコンピュータに物理的にアクセスできるということは、そのコンピュータを「所有」しているということです。

次に、SQL接続がfinallyブロックで必ず閉じられるため、例外がtry/catchブロックで生成されても接続のクリーンアップ処理が適切に行われます。したがって、DoSの脅威が緩和されます。

さて、先ほど約束したとおり、ここで関数呼び出しの先頭にある2つのセキュリティ属性について説明することにしましょう。1番目のSqlClientPermissionAttributeクラスは、SQL Server .NETデータプロバイダが、ユーザーがデータソースへのアクセスに必要なセキュリティレベルを持っていることを保証できるようにします。この例の場合、空のパスワードの使用は禁止されます。このコードと空のパスワードを使用してSQL Serverに接続しようとすると、例外が生成されます。2番目の属性RegistryPermissionAttributeクラスは、どのレジストリキー(1つまたは複数)にどのレベルでアクセスできるのか(読み取りや書き込みなど)を制限します。この例では、接続文字列を保持している1つのキーだけを読み取ることができます。攻撃者がこのコードからレジストリの他の部分にアクセスしようとしても失敗します。

以上のようなしくみをすべて組み合わせることによって、非常に安全なWebサービスを実現できます。これらのしくみを必ず使用し、コードが攻撃に対して安全なように、層を構成するようにしましょう。

まとめ

この章では、Webサービス開発者が使用できるいくつかのセキュリティ機能について説明しました。セキュリティ機能はSOAPプロトコルそのものでは規定されていません。これは、SOAPがHTTP上の使用に限定されていないからです。したがって、アプリケーションでは既存のWebサーバーのセキュリティ機能を利用する必要があります。どの機能を選択するかは、脅威モデル化の過程を通じて収集したデータを基に判断することが重要です。たとえば、基本認証、ダイジェスト認証、または.NET Passport認証を使用すると、クライアントの偽装の脅威を緩和するのに役立ちます。SSL/TLSは、暗号化およびメッセージ認証コードを利用することにより、サーバーの偽装の脅威を緩和できるほか、データの改ざんや情報漏洩といった脅威も緩和できます。また、SSL/TLSはオプションのクライアント認証証明書を使用することにより、クライアント認証のサポートも提供できます。また現在、SOAPメッセージにセキュリティ機能を提供するための作業が進められています。このテクノロジはGlobal XML Web Services Architecture(GXA)と呼ばれています。

最後に、WebアプリケーションやWebサービスの開発者が陥りやすい、よくある間違いをいくつか紹介しました。特に、ユーザーが必ず適正な形式で害のない入力を行うと信じることの危険性について取り上げました。有効性を確認せずに入力内容をそのまま使用した場合、セキュリティ上の深刻な被害が待ちかまえています。以上の忠告を無視した場合の責任は負いかねます。