WebAPIでHTTP認証を実装する

この記事では、WebAPIでのHTTP認証の実装について説明します。WebAPIにHTTP認証を実装する方法は2つあります。これらには以下が含まれます:

  • フォーム認証
  • 基本認証

Windows認証を利用すると、インターネット経由でサービスを公開できないため、Windows認証を実行可能な戦略とは見なしません。

フォーム認証を使用したWebAPIの保護

フォーム認証はASP.Netメンバーシッププロバイダーを使用し、Authorizationヘッダーの代わりに標準のHTTPCookieを使用します。フォーム認証はCookieを使用するため、RESTに適したものではなく、クライアントは、クロスサイト偽造攻撃に対して脆弱なフォーム認証を利用するサービスを利用するためにCookieを管理する必要があります。これが、フォーム認証を使用する場合にCSRF対策を実装する必要がある理由です。フォーム認証は、ユーザーの資格情報を保護するために暗号化を使用しません。したがって、SSLを介してWeb APIを実行しない限り、これは安全な戦略ではありません。

基本認証を使用した安全なWebAPI

基本認証は、ユーザーの資格情報をプレーンテキストでネットワーク経由で送信します。基本認証を使用する場合は、Secure Socket Layer(SSL)を介してWebAPIを使用する必要があります。基本認証を使用する場合、HTTPリクエストのヘッダーでユーザーの認証情報または認証トークンを渡します。サーバー側のサービスは、認証トークンを取得するためにヘッダーを解析する必要があります。要求が有効な要求でない場合、サーバーはHTTP 401を返します。これは、不正な応答を意味します。

アクションフィルターを使用して基本認証を実行する方法を見てみましょう。これを行うには、次のようにクラスを派生させるクラスを作成する必要がありますSystem.Web.Http.Filters.ActionFilterAttribute

public class BasicAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute

    {

        private Boolean IsUserValid(Dictionary credentials)

        {

            if (credentials["UserName"].Equals("joydip") && credentials["Password"].Equals("joydip123"))

                return true;

            return false;

        }

         private Dictionary ParseRequestHeaders(System.Web.Http.Controllers.HttpActionContext actionContext)

        {

            Dictionary credentials = new Dictionary();

             var httpRequestHeader = actionContext.Request.Headers.GetValues("Authorization").FirstOrDefault();

            httpRequestHeader = httpRequestHeader.Substring("Authorization".Length);

             string[] httpRequestHeaderValues = httpRequestHeader.Split(':');

            string username = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[0]));

            string password = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[1]));

             credentials.Add("UserName", username);

            credentials.Add("Password", password);

             return credentials;

        }

         public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)

        {

            try

            {

                if (actionContext.Request.Headers.Authorization == null)

                {

                    actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                }

                else

                {

                     Dictionary credentials = ParseRequestHeaders(actionContext);

                     if (IsUserValid(credentials))

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK);

                    else

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                 }

            }

            catch

            {

                actionContext.Response = new System.Net.Http.HttpResponseMessage

(System.Net.HttpStatusCode.InternalServerError);

            }

        }

    }

認証ヘッダーが存在するかどうかを確認します。そうでない場合は、HTTP401または「無許可」応答が返されます。

次のステップは、クライアントから承認要求ヘッダーを介して渡されたユーザー資格情報を検証することです。その前に、クライアントからWebAPIがどのように呼び出されるかを知っておく必要があります。このために、私はテストメソッドを用意しました。テストメソッドは、HttpClientクラスを使用してWebAPIを呼び出します。ユーザー名は、渡される前にBase64文字列形式に変換されることに注意してください。試験方法は以下のとおりです。

[TestMethod]

        public void BasicAuthenticationTest()

        {

            string username = Convert.ToBase64String(Encoding.UTF8.GetBytes("joydip"));

            string password = Convert.ToBase64String(Encoding.UTF8.GetBytes("joydip123"));

            HttpClient client = new HttpClient();

            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Authorization", username + ":" + password);

            var result = client.GetAsync(new Uri("//localhost//api/default/")).Result;

           Assert.IsTrue(result.IsSuccessStatusCode);

        }

上記のコードスニペットでわかるように、ユーザー資格情報は承認ヘッダーを使用して渡されます。

クライアントの準備ができたので、BasicAuthenicationFilterクラスの実装を完了しましょう。OnActionExecutingメソッド内で、このクラスのヘッダー値を解析し、クライアントから提供された資格情報が一致するかどうかを確認する必要があります。ここでは、ユーザー名とパスワードの値がそれぞれjoydipjoydip123であると仮定します(これらはハードコーディングされています)。これBasicAuthenticationFilterは、ユーザー資格情報の検証を組み込んだクラスの完全なコードです。

public class BasicAuthenticationAttribute : System.Web.Http.Filters.ActionFilterAttribute

    {

        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)

        {

            try

            {

                if (actionContext.Request.Headers.Authorization == null)

                {

                    actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                }

                else

                {

                    actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);

                    var httpRequestHeader = actionContext.Request.Headers.GetValues("Authorization").FirstOrDefault();

                    httpRequestHeader = httpRequestHeader.Substring("Authorization".Length);

                    string[] httpRequestHeaderValues = httpRequestHeader.Split(':');

                    string username = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[0]));

                    string password = Encoding.UTF8.GetString(Convert.FromBase64String(httpRequestHeaderValues[1]));

                    if (username.Equals("joydip") && password.Equals("joydip123"))

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK);

                    else

                        actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);

                }

            }

            catch

            {

                actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);

            }

        }

    }

コントローラクラスでは、属性を適切に指定する必要があります。ここでのBasicAuthentication属性BasicAuthenticationAttributeは、実装したクラスを参照していることに注意してください。

    [BasicAuthentication]

    public class DefaultController : ApiController

    {

        public IEnumerable Get()

        {

            return new string[] { "Joydip", "Kanjilal" };

        }

    }

ここで、少し構成を行う必要があります。認証が機能するように、コントローラーへの呼び出しが適切にフィルター処理されるように属性を構成する必要があります。

 public static class WebApiConfig

    {

        public static void Register(HttpConfiguration config)

        {

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(

                name: "DefaultApi",

                routeTemplate: "api/{controller}/{id}",

                defaults: new { id = RouteParameter.Optional }

            );

            config.Formatters.Remove(config.Formatters.XmlFormatter);

            GlobalConfiguration.Configuration.Filters.Add(new BasicAuthenticationAttribute());

        }

    }

そして、あなたは完了です!テストケースを実行すると、テストに合格します。

とにかく、資格情報がハードコーディングされていないことを確認する必要があります。むしろ、それらはデータベースに保存されるべきであり、それらを取得しOnActionExecutingBasicAuthenticationAttributeクラスのメソッドで検証する必要があります。