Comment sécuriser ASP.NET API Web [fermé]

je veux construire un RESTful service web en utilisant ASP.NET API Web que les développeurs tiers utiliseront pour accéder aux données de mon application.

j'ai beaucoup lu sur outh et il semble être la norme, mais trouver un bon échantillon avec la documentation expliquant comment cela fonctionne (et cela fonctionne réellement!) semble être incroyablement difficile (en particulier pour un internaute novice à OAuth).

est - il un exemple qui construit et fonctionne réellement et montre comment mettre en œuvre cela?

j'ai téléchargé de nombreux échantillons:

  • DotNetOAuth - la documentation est désespérée d'un newbie point de vue
  • Thinktecture - ne peut pas le faire à construire

j'ai aussi regardé les blogs suggérant un système basé sur un jeton simple (comme ce ) - cela semble comme réinventer la roue mais il n'ont l'avantage d'être conceptuellement assez simple.

il semble qu'il y ait beaucoup de questions comme celle-ci sur SO mais pas de bonnes réponses.

qu'est-Ce que tout le monde fait dans cet espace?

353
demandé sur Hossein Narimani Rad 2012-08-02 14:27:19
la source

6 ответов

mise à jour:

j'ai mis ma autre réponse comment utiliser l'authentification JWT pour L'API Web ici pour toute personne intéressée par JWT:

JWT Authentication for Asp.Net Api Web


" nous avons réussi à appliquer l'authentification HMAC pour sécuriser L'API Web, et cela a bien fonctionné. HMAC authentication utilise une clé secrète pour chaque et le serveur savent tous les deux HMAC hachage un message, HMAC256 devrait être utilisé. Dans la plupart des cas, le mot de passe hashé du consommateur est utilisé comme clé secrète.

le message est normalement construit à partir de données de la requête HTTP, ou même de données personnalisées qui sont ajoutées à L'en-tête HTTP, le message peut inclure:

  1. Timestamp: le temps que la demande est envoyée (UTC ou GMT)
  2. HTTP verb: GET, POST, PUT, DELETE.
  3. données post et la chaîne de requête,
  4. URL

sous le capot, authentification HMAC serait:

le consommateur envoie une demande HTTP au serveur web, après avoir créé la signature (sortie de hachage hmac), le modèle de demande HTTP:

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

exemple de requête GET:

GET /webapi.hmac/api/values

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

le message à hachurer pour obtenir la signature:

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

exemple pour la requête POST avec chaîne de requête (la signature ci-dessous n'est pas correcte, juste un exemple)

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

Le message de hachage pour obtenir la signature

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

veuillez noter que les données de forme et la chaîne de requête doivent être en ordre, donc le code sur le serveur obtenir la chaîne de requête et les données de forme pour construire le message correct.

lorsque la requête HTTP arrive au serveur, un filtre d'action d'authentification est implémenté pour analysez la requête pour obtenir des informations: verbe HTTP, timestamp, uri, données de forme et chaîne de requête, puis sur la base de ceux-ci pour construire la signature (utilisez hachage hmac) avec la clé secrète (mot de passe en hachage) sur le serveur.

la clé secrète est obtenue à partir de la base de données avec le nom d'utilisateur sur la demande.

puis le code du serveur compare la signature sur la requête avec la signature construite; si égal, l'authentification est passé, sinon, il a échoué.

Le code pour construire la signature:

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

alors, comment prévenir une attaque de replay?

Ajouter une contrainte pour l'horodatage, quelque chose comme:

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

(servertime: l'heure de la demande à venir à serveur)

et, cache la signature de la requête en mémoire (utiliser MemoryCache, doit conserver dans la limite du temps). Si la requête suivante est livrée avec la même signature que la requête précédente, elle sera rejetée.

le code démo est mis comme ici: https://github.com/cuongle/Hmac.WebApi

266
répondu Cuong Le 2018-01-20 01:27:32
la source

je suggère de commencer par les solutions les plus simples d'abord - peut-être que la simple authentification HTTP Basic + HTTPS est suffisante dans votre scénario.

si ce n'est pas le cas (par exemple, vous ne pouvez pas utiliser https, ou vous avez besoin d'une gestion des clés plus complexe), vous pouvez jeter un coup d'oeil aux solutions basées sur HMAC, comme suggéré par d'autres. Un bon exemple de cette API serait Amazon S3 ( http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html )

j'ai écrit un billet de blog sur l'authentification basée sur HMAC en ASP.NET Web API. Il traite à la fois du service D'API Web et du client D'API Web et le code est disponible sur bitbucket. http://www.piotrwalat.net/hmac-authentication-in-asp-net-web-api /

voici un article sur L'authentification de base dans L'API Web: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers /

Rappelez-vous que si vous devez fournir une API à des tiers, vous serez aussi très probablement responsable de la livraison des bibliothèques clients. L'authentification de base a ici un avantage important car elle est prise en charge sur la plupart des plateformes de programmation. Le HMAC, par contre, n'est pas si normalisé et nécessitera une mise en œuvre sur mesure. Celles-ci devraient être relativement simples, mais nécessitent quand même du travail.

PS. Il y a aussi une option pour utiliser les certificats HTTPS+. http://www.piotrwalat.net/client-certificate-authentication-in-asp-net-web-api-and-windows-store-apps /

29
répondu Piotr Walat 2018-01-20 00:25:04
la source

avez-vous essayé DevDefined.OAuth?

Je l'ai utilisé pour sécuriser mon WebApi avec de l'OAuth à deux pattes. Je l'ai également testé avec succès avec des clients PHP.

il est assez facile d'ajouter le support pour OAuth en utilisant cette bibliothèque. Voici comment vous pouvez mettre en œuvre le fournisseur pour ASP.NET MVC Web API:

1) Récupérez le code source de DevDefined.Outh: https://github.com/bittercoder/DevDefined.OAuth - le plus récent la version permet l'extensibilité OAuthContextBuilder .

2) construisez la bibliothèque et référencez-la dans votre projet D'API Web.

3) Créer un constructeur de contexte personnalisé pour soutenir la construction d'un contexte de 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) Utilisez ce tutoriel pour créer un fournisseur OAuth: http://code.google.com/p/devdefined-tools/wiki/OAuthProvider . Dans la dernière étape (accéder à un exemple de ressource protégée) vous pouvez utilisez ce code dans votre attribut AuthorizationFilterAttribute :

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

j'ai implémenté mon propre fournisseur donc je n'ai pas testé le code ci-dessus (sauf bien sûr le WebApiOAuthContextBuilder que j'utilise dans mon fournisseur) mais il devrait fonctionner correctement.

21
répondu Maksymilian Majer 2013-07-26 17:02:30
la source

API Web introduit un attribut [Authorize] pour fournir la sécurité. Cela peut être fixé globalement (global.asx)

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

ou par contrôleur:

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

bien sûr, votre type d'authentification peut varier et vous pouvez vouloir effectuer votre propre authentification, lorsque cela se produit, vous pouvez trouver utile d'hériter de L'attribut Authorizate et de l'étendre pour répondre à vos exigences:

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

et dans votre contrôleur:

[DemoAuthorize]
public class ValuesController : ApiController{

voici un lien sur d'autres implémentations personnalisées pour les autorisations WebApi:

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

18
répondu Dalorzo 2015-02-28 23:37:06
la source

si vous voulez sécuriser votre API de la manière serveur à Serveur (pas de redirection vers le site web pour authentification à 2 pattes). Vous pouvez consulter le protocole de subvention des justificatifs D'identité des clients OAuth2.

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

j'ai développé une bibliothèque qui peut vous aider à ajouter facilement ce type de support à votre WebAPI. Vous pouvez l'installer comme un paquet NuGet:

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

La bibliothèque des cibles .NET Framework 4.5.

une fois que vous aurez ajouté le paquet à votre projet, il créera un fichier readme à la racine de votre projet. Vous pouvez regarder ce fichier readme pour voir comment configurer/utiliser ce paquet.

santé!

5
répondu Varun Chatterji 2013-05-20 07:47:39
la source

suite à la réponse de @ Cuong Le, mon approche pour empêcher l'attaque de replay serait

/ / chiffrer le temps Unix côté Client en utilisant la clé privée partagée (ou le mot de passe de l'utilisateur)

// l'Envoyer en tant que partie de l'en-tête de requête à un serveur WEB(API)

/ / décrypter le temps Unix au serveur (API WEB) en utilisant la clé privée partagée (ou le mot de passe de l'utilisateur)

/ / Vérifier le décalage horaire entre les Le temps Unix et le temps Unix du serveur, ne doit pas être supérieur à X sec

/ / si le mot de passe ID utilisateur/Hash est correct et que UnixTime déchiffré est dans les x secondes du temps du serveur, alors c'est une requête valide

2
répondu refactor 2015-10-16 14:45:44
la source

Autres questions sur