ASP.NET Web API での認証と承認

Rick Anderson

Web API を作成しましたが、Web API へのアクセスを制御する必要があります。 この一連の記事では、承認されていないユーザーから Web API をセキュリティで保護するためのいくつかのオプションについて説明します。 このシリーズでは、認証と承認の両方について説明します。

  • 認証 では、ユーザーの ID が認識されます。 たとえば、Alice はユーザー名とパスワードを使用してログインし、サーバーはパスワードを使用して Alice を認証します。
  • 承認 とは、ユーザーがアクションの実行を許可されているかどうかを決定することです。 たとえば、Alice にはリソースを取得するアクセス許可がありますが、リソースを作成することはできません。

シリーズの最初の記事では、ASP.NET Web API での認証と承認の概要について説明します。 その他のトピックでは、Web API の一般的な認証シナリオについて説明します。

注

Rick Anderson、Levi Broderick、Barry Dorrans、Tom Dykstra、Hongmei Ge、David Matson、Daniel Roth、Tim Teebken など、このシリーズをレビューし、貴重なフィードバックを提供してくれた方々に感謝します。

認証

Web API は、ホストで認証が行われることを前提としています。 Web ホスティングの場合、ホストは IIS であり、認証に HTTP モジュールを使用します。 IIS または ASP.NET に組み込まれている認証モジュールのいずれかを使用するようにプロジェクトを構成することも、カスタム認証を実行する独自の HTTP モジュールを作成することもできます。

ホストがユーザーを認証すると、 プリンシパルが作成されます。これは、コードが実行されているセキュリティ コンテキストを表す IPrincipal オブジェクトです。 ホストは、 Thread.CurrentPrincipal を設定して、現在のスレッドにプリンシパルをアタッチします。 プリンシパルには、ユーザーに関する情報を含む関連付けられた ID オブジェクトが含まれています。 ユーザーが認証されている場合、 Identity.IsAuthenticated プロパティはtrue を返します。 匿名要求の場合、 IsAuthenticated は false を返 します。 プリンシパルの詳細については、「 Role-Based セキュリティ」を参照してください。

認証用の HTTP メッセージ ハンドラー

認証にホストを使用する代わりに、認証ロジックを HTTP メッセージ ハンドラーに配置できます。 その場合、メッセージ ハンドラーは HTTP 要求を調べ、プリンシパルを設定します。

認証にメッセージ ハンドラーを使用する必要がある場合 いくつかのトレードオフを次に示します。

  • HTTP モジュールには、ASP.NET パイプラインを通過するすべての要求が表示されます。 メッセージ ハンドラーには、Web API にルーティングされた要求のみが表示されます。
  • ルートごとのメッセージ ハンドラーを設定できます。これにより、特定のルートに認証スキームを適用できます。
  • HTTP モジュールは IIS に固有です。 メッセージ ハンドラーはホストに依存しないため、Web ホスティングとセルフホスティングの両方で使用できます。
  • HTTP モジュールは、IIS のログ記録や監査などを行います。
  • HTTP モジュールは、パイプラインの前の方で実行されます。 メッセージ ハンドラーで認証を処理する場合、プリンシパルはハンドラーが実行されるまで設定されません。 さらに、応答がメッセージ ハンドラーから離れると、プリンシパルは前のプリンシパルに戻ります。

一般に、セルフホスティングをサポートする必要がない場合は、HTTP モジュールを使用することをお勧めします。 セルフホスティングをサポートする必要がある場合は、メッセージ ハンドラーを検討してください。

プリンシパルの設定

アプリケーションでカスタム認証ロジックを実行する場合は、プリンシパルを次の 2 つの場所に設定する必要があります。

  • Thread.CurrentPrincipal。 このプロパティは、.NET でスレッドのプリンシパルを設定する標準的な方法です。
  • HttpContext.Current.User。 このプロパティは、ASP.NET に固有です。

次のコードは、プリンシパルを設定する方法を示しています。

private void SetPrincipal(IPrincipal principal)
{
    Thread.CurrentPrincipal = principal;
    if (HttpContext.Current != null)
    {
        HttpContext.Current.User = principal;
    }
}

Web ホスティングの場合は、両方の場所でプリンシパルを設定する必要があります。そうしないと、セキュリティ コンテキストが不整合になる可能性があります。 ただし、セルフホスティングの場合、 HttpContext.Current は null です。 そのため、コードがホストに依存しないようにするには、次に示すように、 HttpContext.Current に割り当てる前に null を確認します。

認可

承認は、後でコントローラーに近いパイプラインで行われます。 これにより、リソースへのアクセスを許可するときに、より詳細な選択を行うことができます。

  • 承認フィルターは 、コントローラー アクションの前に実行されます。 要求が承認されていない場合、フィルターはエラー応答を返し、アクションは呼び出されません。
  • コントローラー アクション内で、 ApiController.User プロパティから現在のプリンシパルを取得できます。 たとえば、ユーザー名に基づいてリソースの一覧をフィルター処理し、そのユーザーに属するリソースのみを返す場合があります。

認証と承認のパイプラインの図。

[Authorize] 属性の使用

Web API には、組み込みの承認フィルター AuthorizeAttribute が用意されています。 このフィルターは、ユーザーが認証されているかどうかを確認します。 そうでない場合は、アクションを呼び出さずに HTTP 状態コード 401 (Unauthorized) が返されます。

フィルターは、グローバル、コントローラー レベル、または個々のアクションのレベルで適用できます。

グローバル: すべての Web API コントローラーのアクセスを制限するには、 AuthorizeAttribute フィルターをグローバル フィルターの一覧に追加します。

public static void Register(HttpConfiguration config)
{
    config.Filters.Add(new AuthorizeAttribute());
}

コントローラー: 特定のコントローラーのアクセスを制限するには、フィルターを属性としてコントローラーに追加します。

// Require authorization for all actions on the controller.
[Authorize]
public class ValuesController : ApiController
{
    public HttpResponseMessage Get(int id) { ... }
    public HttpResponseMessage Post() { ... }
}

アクション: 特定のアクションのアクセスを制限するには、アクション メソッドに属性を追加します。

public class ValuesController : ApiController
{
    public HttpResponseMessage Get() { ... }

    // Require authorization for a specific action.
    [Authorize]
    public HttpResponseMessage Post() { ... }
}

または、 [AllowAnonymous] 属性を使用して、コントローラーを制限し、特定のアクションへの匿名アクセスを許可することもできます。 次の例では、 Post メソッドは制限されていますが、 Get メソッドでは匿名アクセスが許可されています。

[Authorize]
public class ValuesController : ApiController
{
    [AllowAnonymous]
    public HttpResponseMessage Get() { ... }

    public HttpResponseMessage Post() { ... }
}

前の例では、フィルターを使用すると、認証されたユーザーは制限されたメソッドにアクセスできます。匿名ユーザーのみが除外されます。特定のユーザーまたは特定のロールのユーザーへのアクセスを制限することもできます。

// Restrict by user:
[Authorize(Users="Alice,Bob")]
public class ValuesController : ApiController
{
}
   
// Restrict by role:
[Authorize(Roles="Administrators")]
public class ValuesController : ApiController
{
}

注

Web API コントローラーの AuthorizeAttribute フィルターは、 System.Web.Http 名前空間にあります。 System.Web.Mvc 名前空間には MVC コントローラー用の同様のフィルターがあり、Web API コントローラーと互換性がありません。

カスタム承認フィルター

カスタム承認フィルターを作成するには、次のいずれかの種類から派生します。

  • AuthorizeAttribute。 このクラスを拡張して、現在のユーザーとユーザーのロールに基づいて承認ロジックを実行します。
  • AuthorizationFilterAttribute。 このクラスを拡張して、現在のユーザーまたはロールに基づくとは限らない同期承認ロジックを実行します。
  • IAuthorizationFilter。 非同期承認ロジックを実行するには、このインターフェイスを実装します。たとえば、承認ロジックが非同期 I/O またはネットワーク呼び出しを行う場合などです。 (承認ロジックが CPU にバインドされている場合は、非同期メソッドを記述する必要がないため、 AuthorizationFilterAttribute から派生する方が簡単です)。

次の図は、 AuthorizeAttribute クラスのクラス階層を示しています。

Authorize Attribute クラスのクラス階層の図。

Authorize Attribute クラスのクラス階層の図。 承認属性は下部にあり、矢印は承認フィルター属性を指し、矢印は上部の I 承認フィルターを指しています。

コントローラー アクション内での承認

場合によっては、要求の続行を許可しつつ、主体に基づいて動作を変更することがあります。 たとえば、返される情報は、ユーザーのロールによって変わる場合があります。 コントローラー メソッド内では、 ApiController.User プロパティから現在のプリンシパルを取得できます。

public HttpResponseMessage Get()
{
    if (User.IsInRole("Administrators"))
    {
        // ...
    }
}