c# authentication oauth2 - Cómo asegurar una API web ASP.NET




3 Answers

Actualizar:

He puesto mi otra respuesta sobre cómo usar la autenticación JWT para la API web aquí para cualquier persona interesada en JWT:

Autenticación JWT para Asp.Net Web Api

Hemos logrado aplicar la autenticación HMAC para asegurar la API web, y funcionó bien. La autenticación HMAC utiliza una clave secreta para cada consumidor que, tanto el consumidor como el servidor, conocen para enviar un mensaje a Hmac, debe usarse HMAC256. La mayoría de los casos, la contraseña de hash del consumidor se utiliza como una clave secreta.

El mensaje normalmente se genera a partir de datos en la solicitud HTTP, o incluso datos personalizados que se agregan al encabezado HTTP, el mensaje puede incluir:

  1. Marca de tiempo: hora en que se envía la solicitud (UTC o GMT)
  2. Verbo HTTP: GET, POST, PUT, DELETE.
  3. publicar datos y cadena de consulta,
  4. URL

Bajo el capó, la autenticación HMAC sería:

El consumidor envía una solicitud HTTP al servidor web, después de crear la firma (resultado de hash hmac), la plantilla de solicitud HTTP:

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

Ejemplo para solicitud GET:

GET /webapi.hmac/api/values

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

El mensaje a hash para obtener la firma:

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

Ejemplo para solicitud POST con cadena de consulta (la firma a continuación no es correcta, solo un ejemplo)

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

El mensaje al hash para obtener la firma.

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

Tenga en cuenta que los datos del formulario y la cadena de consulta deben estar en orden, de modo que el código en el servidor obtenga la cadena de consulta y los datos del formulario para generar el mensaje correcto.

Cuando la solicitud HTTP llega al servidor, se implementa un filtro de acción de autenticación para analizar la solicitud para obtener información: verbo HTTP, marca de tiempo, uri, datos de formulario y cadena de consulta, luego, en función de estos, para crear la firma (use hmac hash) con el secreto clave (contraseña hash) en el servidor.

La clave secreta se obtiene de la base de datos con el nombre de usuario en la solicitud.

Luego, el código del servidor compara la firma en la solicitud con la firma construida; si es igual, la autenticación se pasa, de lo contrario, falló.

El código para construir la firma:

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

Entonces, ¿cómo prevenir el ataque de repetición?

Añadir restricción para la marca de tiempo, algo así como:

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

(tiempo de servicio: tiempo de solicitud que llega al servidor)

Y, caché la firma de la solicitud en la memoria (use MemoryCache, debe mantenerse en el límite de tiempo). Si la siguiente solicitud viene con la misma firma que la solicitud anterior, será rechazada.

El código de demostración se pone aquí: https://github.com/cuongle/Hmac.WebApi

token rest

Quiero crear un servicio web RESTful utilizando la API web de ASP.NET que los desarrolladores externos usarán para acceder a los datos de mi aplicación.

He leído mucho sobre OAuth y parece ser el estándar, pero encontrar una buena muestra con documentación que explique cómo funciona (¡y eso realmente funciona!) Parece ser increíblemente difícil (especialmente para un novato en OAuth).

¿Hay una muestra que realmente construye, funciona y muestra cómo implementar esto?

He descargado numerosas muestras:

  • DotNetOAuth: la documentación no tiene remedio desde la perspectiva de un novato
  • Thinktecture - no se puede construir

También he visto blogs que sugieren un esquema simple basado en token (como this ); esto parece re-inventar la rueda pero tiene la ventaja de ser conceptualmente bastante simple.

Parece que hay muchas preguntas como esta en SO pero no hay buenas respuestas.

¿Qué hacen todos en este espacio?




¿Has probado DevDefined.OAuth?

Lo he usado para asegurar mi WebApi con 2-Legged OAuth. También lo he probado con éxito con clientes PHP.

Es muy fácil agregar soporte para OAuth usando esta biblioteca. Aquí le mostramos cómo puede implementar el proveedor para ASP.NET MVC Web API:

1) Obtenga el código fuente de DevDefined.OAuth: https://github.com/bittercoder/DevDefined.OAuth : la versión más reciente permite la extensibilidad de OAuthContextBuilder .

2) Construya la biblioteca y haga referencia a ella en su proyecto de API web.

3) Cree un generador de contextos personalizado para admitir la creación de un contexto desde 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) Utilice este tutorial para crear un proveedor de OAuth: http://code.google.com/p/devdefined-tools/wiki/OAuthProvider . En el último paso (Acceso al Ejemplo de recurso protegido) puede usar este código en su atributo de AuthorizationFilterAttribute atributos:

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

He implementado mi propio proveedor, así que no he probado el código anterior (excepto, por supuesto, el WebApiOAuthContextBuilder que estoy usando en mi proveedor) pero debería funcionar bien.




Si desea proteger su API de un servidor a otro (no hay redirección al sitio web para la autenticación de 2 patas). Puede consultar el protocolo OAuth2 Client Credentials Grant.

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

He desarrollado una biblioteca que puede ayudarlo a agregar fácilmente este tipo de soporte a su WebAPI. Puedes instalarlo como un paquete NuGet:

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

La biblioteca apunta a .NET Framework 4.5.

Una vez que agregue el paquete a su proyecto, creará un archivo Léame en la raíz de su proyecto. Puede ver ese archivo Léame para ver cómo configurar / usar este paquete.

¡Aclamaciones!




Related