c# apicontroller - So sichern Sie eine ASP.NET-Web-API




response custom (6)

Hast du DevDefined.OAuth ausprobiert?

Ich habe es verwendet, um meine WebApi mit 2-Legged OAuth zu sichern. Ich habe es auch erfolgreich mit PHP-Clients getestet.

Es ist ziemlich einfach, Unterstützung für OAuth mit dieser Bibliothek hinzuzufügen. So können Sie den Anbieter für die ASP.NET MVC-Web-API implementieren:

1) Holen Sie den Quellcode von DevDefined.OAuth: https://github.com/bittercoder/DevDefined.OAuth - die neueste Version ermöglicht OAuthContextBuilder Erweiterbarkeit.

2) Erstellen Sie die Bibliothek und verweisen Sie sie in Ihrem Web-API-Projekt.

3) Erstellen Sie einen benutzerdefinierten Context Builder, um das Erstellen eines Kontexts aus 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) Verwenden Sie dieses Lernprogramm zum Erstellen eines OAuth-Anbieters: http://code.google.com/p/devdefined-tools/wiki/OAuthProvider . Im letzten Schritt (Zugriff auf ein geschütztes Ressourcen-Beispiel) können Sie diesen Code in Ihrem AuthorizationFilterAttribute Attribut verwenden:

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()
            };
    }
}

Ich habe meinen eigenen Provider implementiert, also habe ich den obigen Code nicht getestet (außer natürlich den WebApiOAuthContextBuilder den ich in meinem Provider verwende), aber es sollte gut funktionieren.

Ich möchte einen RESTful- Webdienst mithilfe der ASP.NET-Web-API erstellen, die von Drittentwicklern für den Zugriff auf die Daten meiner Anwendung verwendet wird.

Ich habe ziemlich viel über OAuth gelesen und es scheint der Standard zu sein, aber ein gutes Beispiel mit Dokumentation zu finden, die erklärt, wie es funktioniert (und das tatsächlich funktioniert!) Scheint unglaublich schwierig zu sein (besonders für einen Neuling von OAuth).

Gibt es ein Beispiel, das tatsächlich erstellt und funktioniert und zeigt, wie dies implementiert wird?

Ich habe zahlreiche Beispiele heruntergeladen:

  • DotNetOAuth - Dokumentation ist hoffnungslos aus der Perspektive eines Neulings
  • Thinktecture - kann es nicht bauen

Ich habe mir auch Blogs angeschaut, die ein einfaches tokenbasiertes Schema (wie this ) vorschlagen - das scheint das Rad neu zu erfinden, aber es hat den Vorteil, konzeptionell ziemlich einfach zu sein.

Es scheint so viele Fragen wie SO zu geben, aber keine guten Antworten.

Was machen alle in diesem Raum?


Die Web-API hat ein Attribut [Authorize] , um Sicherheit zu bieten. Dies kann global eingestellt werden (global.asx)

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

Oder pro Controller:

[Authorize]
public class ValuesController : ApiController{
...

Natürlich kann Ihre Art der Authentifizierung variieren und Sie möchten möglicherweise Ihre eigene Authentifizierung durchführen. In diesem Fall können Sie nützlich sein, das Attribut Autorize zu erben und es so zu erweitern, dass es Ihren Anforderungen entspricht:

public class DemoAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        if (Authorize(actionContext))
        {
            return;
        }
        HandleUnauthorizedRequest(actionContext);
    }

    protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
        throw new HttpResponseException(challengeMessage);
    }

    private bool Authorize(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        try
        {
            var someCode = (from h in actionContext.Request.Headers where h.Key == "demo" select h.Value.First()).FirstOrDefault();
            return someCode == "myCode";
        }
        catch (Exception)
        {
            return false;
        }
    }
}

Und in deinem Controller:

[DemoAuthorize]
public class ValuesController : ApiController{

Hier ist ein Link zu anderen benutzerdefinierten Implementierungen für WebApi-Autorisierungen:

http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-membership-provider/


Aktualisieren:

Ich habe eine andere Antwort gegeben, wie man JWT-Authentifizierung für Web-API hier für jeden benutzt, der an JWT interessiert ist:

JWT-Authentifizierung für Asp.Net Web Api

Wir haben es geschafft, die HMAC-Authentifizierung auf die sichere Web-API anzuwenden, und es hat funktioniert. Die HMAC-Authentifizierung verwendet einen geheimen Schlüssel für jeden Verbraucher, der sowohl dem Verbraucher als auch dem Server weiß, dass er eine Nachricht hmac-hashen soll, wobei HMAC256 verwendet werden sollte. In den meisten Fällen wird das Hash-Passwort des Konsumenten als geheimer Schlüssel verwendet.

Die Nachricht wird normalerweise aus Daten in der HTTP-Anforderung oder sogar benutzerdefinierten Daten erstellt, die zum HTTP-Header hinzugefügt werden. Die Nachricht könnte Folgendes enthalten:

  1. Timestamp: Zeit, zu der die Anfrage gesendet wird (UTC oder GMT)
  2. HTTP-Verb: GET, POST, PUT, DELETE.
  3. Postdaten und Abfragezeichenfolge,
  4. URL

Unter der Haube würde HMAC-Authentifizierung sein:

Consumer sendet eine HTTP-Anfrage an den Webserver, nach dem Erstellen der Signatur (Ausgabe von hmac-Hash), der Vorlage der HTTP-Anfrage:

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

Beispiel für eine GET-Anfrage:

GET /webapi.hmac/api/values

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

Die Nachricht zum Hash, um die Signatur zu erhalten:

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

Beispiel für eine POST-Anfrage mit Abfragezeichenfolge (Signatur unten ist nicht korrekt, nur ein Beispiel)

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

Die Nachricht zum Hash, um die Signatur zu erhalten

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

Beachten Sie, dass die Formulardaten und die Abfragezeichenfolge in der richtigen Reihenfolge sein sollten, damit der Code auf dem Server Abfragezeichenfolge und Formulardaten zum Erstellen der richtigen Nachricht erhält.

Wenn eine HTTP-Anfrage an den Server kommt, wird ein Authentifizierungs-Aktionsfilter implementiert, um die Anfrage zu analysieren, um Informationen zu erhalten: HTTP-Verb, Zeitstempel, URI, Formulardaten und Abfragezeichenfolge, basierend darauf, um Signatur mit dem Geheimnis zu erstellen Schlüssel (Hash-Passwort) auf dem Server.

Der geheime Schlüssel wird von der Datenbank mit dem Benutzernamen auf der Anfrage erhalten.

Dann vergleicht der Server-Code die Signatur der Anfrage mit der erstellten Signatur; Wenn sie gleich ist, wird die Authentifizierung übergeben, andernfalls ist sie fehlgeschlagen.

Der Code zum Erstellen der Signatur:

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;
}

Wie kann man Replay-Attacken verhindern?

Fügen Sie eine Einschränkung für den Zeitstempel hinzu, beispielsweise:

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

(Serverzeit: Uhrzeit der Anfrage beim Server)

Und, die Signatur der Anfrage im Speicher zwischenspeichern (verwenden Sie MemoryCache, sollte in der Begrenzung der Zeit halten). Wenn die nächste Anfrage dieselbe Signatur mit der vorherigen Anfrage enthält, wird sie abgelehnt.

Der Demo-Code wird wie folgt eingegeben: https://github.com/cuongle/Hmac.WebApi


in Fortsetzung zu @ Cuong Les Antwort wäre mein Ansatz, Wiederholungsangriffe zu verhindern

// Verschlüsseln Sie die Unix-Zeit auf Client-Seite mit dem freigegebenen privaten Schlüssel (oder Benutzer-Passwort)

// Senden Sie es als Teil des Anfrage-Headers zum Server (WEB-API)

// Entschlüsselung der Unix-Zeit am Server (WEB-API) mit dem freigegebenen privaten Schlüssel (oder dem Benutzerpasswort)

// Überprüfen Sie den Zeitunterschied zwischen der Unixzeit des Clients und der Unixzeit des Servers, sollte nicht größer als x Sek. Sein

// Wenn Benutzer-ID / Hash-Passwort korrekt sind und die entschlüsselte UnixTime innerhalb von x Sek. der Serverzeit liegt, dann ist dies eine gültige Anfrage


Wenn Sie Ihre API in einem Server auf Server-Art sichern möchten (keine Umleitung zur Website für die 2-Being-Authentifizierung). Sie können das OAuth2 Client Credentials Grant-Protokoll anzeigen.

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

Ich habe eine Bibliothek entwickelt, mit der Sie diese Art von Unterstützung für Ihr WebAPI einfach hinzufügen können. Sie können es als NuGet-Paket installieren:

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

Die Bibliothek zielt auf .NET Framework 4.5 ab.

Nachdem Sie das Paket zu Ihrem Projekt hinzugefügt haben, wird eine Readme-Datei im Stammverzeichnis Ihres Projekts erstellt. Sie können sich diese Readme-Datei ansehen, um zu sehen, wie Sie dieses Paket konfigurieren / verwenden.

Prost!


foreach (Suit suit in Enum.GetValues(typeof(Suit))) { }

Ich habe vage Gerüchte gehört, dass dies extrem langsam ist. Weiß jemand? - Orion Edwards 15. Oktober 08 um 01.31 Uhr 7

Ich denke, das Cachen des Arrays würde es erheblich beschleunigen. Es sieht so aus, als ob Sie jedes Mal ein neues Array erhalten (durch Reflektion). Lieber:

Array enums = Enum.GetValues(typeof(Suit));
foreach (Suit suitEnum in enums) 
{
    DoSomething(suitEnum);
}

Das ist wenigstens ein bisschen schneller, ja?





c# asp.net-mvc oauth asp.net-web-api