404

サードパーティの開発者がアプリケーションのデータにアクセスするために使用する ASP.NET Web API を使用して、 RESTful Web サービスを構築したいと考えています。

私はOAuthについてかなり多くのことを読んできましたが、それは標準のようですが、それがどのように機能するかを説明するドキュメント (そして実際に機能します!) を含む適切なサンプルを見つけることは、非常に難しいようです (特に OAuth の初心者にとって)。

実際にビルドして動作し、これを実装する方法を示すサンプルはありますか?

多数のサンプルをダウンロードしました:

  • DotNetOAuth - 初心者の観点からすると、ドキュメントは絶望的です
  • Thinktecture - ビルドできない

また、シンプルなトークンベースのスキーム (このような)を提案しているブログも見てきました。

SOにはこのような質問がたくさんあるようですが、良い答えはありません。

みんなはこの空間で何をしているの?

4

6 に答える 6

299

アップデート:

JWTに関心のある人のために、 ASP.NETWebAPIにJWT認証を使用する方法を他の回答に追加しました。


HMAC認証をセキュリティで保護されたWebAPIに適用することができましたが、問題なく機能しました。HMAC認証では、コンシューマーとサーバーの両方がメッセージをhmacハッシュすることを知っているコンシューマーごとに秘密鍵を使用します。HMAC256を使用する必要があります。ほとんどの場合、コンシューマーのハッシュ化されたパスワードが秘密鍵として使用されます。

メッセージは通常、HTTPリクエストのデータ、またはHTTPヘッダーに追加されたカスタマイズされたデータから作成されます。メッセージには次のものが含まれる場合があります。

  1. タイムスタンプ:リクエストが送信された時刻(UTCまたはGMT)
  2. HTTP動詞:GET、POST、PUT、DELETE。
  3. 投稿データとクエリ文字列、
  4. URL

内部的には、HMAC認証は次のようになります。

コンシューマーは、HTTP要求のテンプレートである署名(hmacハッシュの出力)を構築した後、HTTP要求をWebサーバーに送信します。

User-Agent: {agent}   
Host: {host}   
Timestamp: {timestamp}
Authentication: {username}:{signature}

GETリクエストの例:

GET /webapi.hmac/api/values

User-Agent: Fiddler    
Host: localhost    
Timestamp: Thursday, August 02, 2012 3:30:32 PM 
Authentication: cuongle:LohrhqqoDy6PhLrHAXi7dUVACyJZilQtlDzNbLqzXlw=

署名を取得するためにハッシュするメッセージ:

GET\n
Thursday, August 02, 2012 3:30:32 PM\n
/webapi.hmac/api/values\n

クエリ文字列を使用したPOSTリクエストの例(以下の署名は正しくありません。単なる例です)

POST /webapi.hmac/api/values?key2=value2

User-Agent: Fiddler    
Host: localhost    
Content-Type: application/x-www-form-urlencoded
Timestamp: Thursday, August 02, 2012 3:30:32 PM 
Authentication: cuongle:LohrhqqoDy6PhLrHAXi7dUVACyJZilQtlDzNbLqzXlw=

key1=value1&key3=value3

署名を取得するためにハッシュするメッセージ

GET\n
Thursday, August 02, 2012 3:30:32 PM\n
/webapi.hmac/api/values\n
key1=value1&key2=value2&key3=value3

フォームデータとクエリ文字列は順番に並んでいる必要があるため、サーバー上のコードはクエリ文字列とフォームデータを取得して正しいメッセージを作成することに注意してください。

HTTPリクエストがサーバーに到着すると、認証アクションフィルターが実装され、リクエストを解析して情報(HTTP動詞、タイムスタンプ、uri、フォームデータ、クエリ文字列)を取得し、これらに基づいてシークレットを使用して署名を作成します(hmacハッシュを使用)。サーバー上のキー(ハッシュされたパスワード)。

秘密鍵は、リクエスト時にユーザー名を使用してデータベースから取得されます。

次に、サーバーコードは、リクエストの署名と作成された署名を比較します。等しい場合は認証に合格し、そうでない場合は失敗します。

署名を作成するためのコード:

private static string ComputeHash(string hashedPassword, string message)
{
    var key = Encoding.UTF8.GetBytes(hashedPassword.ToUpper());
    string hashString;

    using (var hmac = new HMACSHA256(key))
    {
        var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(message));
        hashString = Convert.ToBase64String(hash);
    }

    return hashString;
}

では、リプレイ攻撃を防ぐ方法は?

次のようなタイムスタンプの制約を追加します。

servertime - X minutes|seconds  <= timestamp <= servertime + X minutes|seconds 

(servertime:サーバーにリクエストが到着する時間)

そして、リクエストの署名をメモリにキャッシュします(MemoryCacheを使用し、制限時間内に保持する必要があります)。次のリクエストに前のリクエストと同じ署名が付いている場合、そのリクエストは拒否されます。

デモコードは次のように配置されます: https ://github.com/cuongle/Hmac.WebApi

于 2012-08-02T17:02:43.377 に答える
34

最初に最も簡単なソリューションから始めることをお勧めします。シナリオでは、単純な HTTP 基本認証 + HTTPS で十分かもしれません。

そうでない場合 (たとえば、https を使用できない、またはより複雑なキー管理が必要な場合)、他の人が提案する HMAC ベースのソリューションを検討することができます。このような API の良い例は、Amazon S3 ( http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html )です。

ASP.NET Web API での HMAC ベースの認証に関するブログ記事を書きました。Web API サービスと Web API クライアントの両方について説明しており、コードは bitbucket で入手できます。http://www.piotrwalat.net/hmac-authentication-in-asp-net-web-api/

Web API の基本認証に関する投稿は次のとおりです: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/

サード パーティに API を提供する場合は、クライアント ライブラリの提供も担当する可能性が高いことに注意してください。基本認証は、ほとんどのプログラミング プラットフォームですぐにサポートされるため、ここでは大きな利点があります。一方、HMAC はそれほど標準化されておらず、カスタム実装が必要になります。これらは比較的簡単ですが、まだ作業が必要です。

PS。HTTPS + 証明書を使用するオプションもあります。http://www.piotrwalat.net/client-certificate-authentication-in-asp-net-web-api-and-windows-store-apps/

于 2013-03-01T12:11:26.010 に答える
23

DevDefined.OAuth を試しましたか?

2-Legged OAuth で WebApi を保護するために使用しました。また、PHP クライアントでのテストにも成功しました。

このライブラリを使用して OAuth のサポートを追加するのは非常に簡単です。ASP.NET MVC Web API のプロバイダーを実装する方法を次に示します。

1) DevDefined.OAuth のソース コードを取得します: https://github.com/bittercoder/DevDefined.OAuth - 最新バージョンではOAuthContextBuilder拡張性が考慮されています。

2) ライブラリをビルドし、Web API プロジェクトで参照します。

3) からのコンテキストの構築をサポートするカスタム コンテキスト ビルダーを作成しますHttpRequestMessage

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Web;

using DevDefined.OAuth.Framework;

public class WebApiOAuthContextBuilder : OAuthContextBuilder
{
    public WebApiOAuthContextBuilder()
        : base(UriAdjuster)
    {
    }

    public IOAuthContext FromHttpRequest(HttpRequestMessage request)
    {
        var context = new OAuthContext
            {
                RawUri = this.CleanUri(request.RequestUri), 
                Cookies = this.CollectCookies(request), 
                Headers = ExtractHeaders(request), 
                RequestMethod = request.Method.ToString(), 
                QueryParameters = request.GetQueryNameValuePairs()
                    .ToNameValueCollection(), 
            };

        if (request.Content != null)
        {
            var contentResult = request.Content.ReadAsByteArrayAsync();
            context.RawContent = contentResult.Result;

            try
            {
                // the following line can result in a NullReferenceException
                var contentType = 
                    request.Content.Headers.ContentType.MediaType;
                context.RawContentType = contentType;

                if (contentType.ToLower()
                    .Contains("application/x-www-form-urlencoded"))
                {
                    var stringContentResult = request.Content
                        .ReadAsStringAsync();
                    context.FormEncodedParameters = 
                        HttpUtility.ParseQueryString(stringContentResult.Result);
                }
            }
            catch (NullReferenceException)
            {
            }
        }

        this.ParseAuthorizationHeader(context.Headers, context);

        return context;
    }

    protected static NameValueCollection ExtractHeaders(
        HttpRequestMessage request)
    {
        var result = new NameValueCollection();

        foreach (var header in request.Headers)
        {
            var values = header.Value.ToArray();
            var value = string.Empty;

            if (values.Length > 0)
            {
                value = values[0];
            }

            result.Add(header.Key, value);
        }

        return result;
    }

    protected NameValueCollection CollectCookies(
        HttpRequestMessage request)
    {
        IEnumerable<string> values;

        if (!request.Headers.TryGetValues("Set-Cookie", out values))
        {
            return new NameValueCollection();
        }

        var header = values.FirstOrDefault();

        return this.CollectCookiesFromHeaderString(header);
    }

    /// <summary>
    /// Adjust the URI to match the RFC specification (no query string!!).
    /// </summary>
    /// <param name="uri">
    /// The original URI. 
    /// </param>
    /// <returns>
    /// The adjusted URI. 
    /// </returns>
    private static Uri UriAdjuster(Uri uri)
    {
        return
            new Uri(
                string.Format(
                    "{0}://{1}{2}{3}", 
                    uri.Scheme, 
                    uri.Host, 
                    uri.IsDefaultPort ?
                        string.Empty :
                        string.Format(":{0}", uri.Port), 
                    uri.AbsolutePath));
    }
}

4) このチュートリアルを使用して OAuth プロバイダーを作成します: http://code.google.com/p/devdefined-tools/wiki/OAuthProviderAuthorizationFilterAttribute最後のステップ (保護されたリソースへのアクセスの例) では、属性で次のコードを使用できます。

public override void OnAuthorization(HttpActionContext actionContext)
{
    // the only change I made is use the custom context builder from step 3:
    OAuthContext context = 
        new WebApiOAuthContextBuilder().FromHttpRequest(actionContext.Request);

    try
    {
        provider.AccessProtectedResourceRequest(context);

        // do nothing here
    }
    catch (OAuthException authEx)
    {
        // the OAuthException's Report property is of the type "OAuthProblemReport", it's ToString()
        // implementation is overloaded to return a problem report string as per
        // the error reporting OAuth extension: http://wiki.oauth.net/ProblemReporting
        actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
            {
               RequestMessage = request, ReasonPhrase = authEx.Report.ToString()
            };
    }
}

私は独自のプロバイダーを実装したので、上記のコードをテストしていません (もちろん、 WebApiOAuthContextBuilder私のプロバイダーで使用しているものは除きます) が、正常に動作するはずです。

于 2012-08-13T07:25:28.423 に答える
5

サーバーからサーバーへの方法で API を保護する場合 (2 レッグ認証の場合、Web サイトへのリダイレクトはありません)。OAuth2 Client Credentials Grant プロトコルを見ることができます。

https://dev.twitter.com/docs/auth/application-only-auth

この種のサポートを WebAPI に簡単に追加するのに役立つライブラリを開発しました。NuGet パッケージとしてインストールできます。

https://nuget.org/packages/OAuth2ClientCredentialsGrant/1.0.0.0

ライブラリは .NET Framework 4.5 を対象としています。

パッケージをプロジェクトに追加すると、プロジェクトのルートに readme ファイルが作成されます。その readme ファイルを見て、このパッケージを構成/使用する方法を確認できます。

乾杯!

于 2013-05-20T03:47:39.013 に答える
4

@ Cuong Le's answer に続いて、リプレイ攻撃を防ぐための私のアプローチは次のようになります

// 共有秘密鍵 (またはユーザーのパスワード) を使用して、クライアント側で Unix Time を暗号化します

// リクエストヘッダーの一部としてサーバー(WEB API)に送信

// サーバー (WEB API) で共有秘密鍵 (またはユーザーのパスワード) を使用して Unix Time を復号化します。

// クライアントの Unix 時間とサーバーの Unix 時間の時間差をチェックします。x 秒を超えてはいけません

// ユーザー ID/ハッシュ パスワードが正しく、復号化された UnixTime がサーバー時間の x 秒以内にある場合、それは有効な要求です

于 2015-10-16T11:36:21.280 に答える