Quiero construir una Descanso servicio web utilizando ASP.NET Web API que los desarrolladores de terceros se utilizan para acceder a mis datos de la aplicación.

He leído bastante acerca de OAuth y parece ser la norma, pero encontrar una buena muestra con documentación que explica cómo funciona (y que en realidad hace el trabajo!) parece ser increíblemente difícil (sobre todo para un novato OAuth).

Hay una muestra de que la realidad se construye y funciona y se muestra cómo implementar esto?

He descargado numerosas muestras:

  • DotNetOAuth – la documentación es desesperada de un novato perspectiva
  • Thinktecture – no se puede llegar a construir

También he mirado en los blogs lo que sugiere una simple token basado en el esquema (como este) – esto parece como re-inventar la rueda, pero tiene la ventaja de ser conceptualmente bastante simple.

Parece que hay muchas preguntas como esta en algo ASÍ, pero no hay respuestas buenas.

Qué está todo el mundo haciendo en este espacio?

6 Comentarios

  1. 285

    Actualización:

    He añadido este enlace a mi otra respuesta cómo utilizar JWT para la autenticación ASP.NET Web API aquí para cualquier persona interesada en JWT.


    Hemos conseguido aplicar la autenticación HMAC para garantizar la API de Web, y funcionó bien. HMAC autenticación utiliza una clave secreta para cada consumidor que tanto el consumidor como el servidor sabe a hmac el hash de un mensaje, HMAC256 debe ser utilizado. La mayoría de los casos, el hash de la contraseña del consumidor se utiliza como una clave secreta.

    El mensaje normalmente se construye a partir de los datos de la solicitud HTTP, o incluso personalizar los datos que se añade a la cabecera HTTP, el mensaje puede incluir:

    1. La marca de tiempo: el tiempo que se envía la solicitud (UTC o GMT)
    2. Verbo HTTP: GET, POST, PUT, DELETE.
    3. posterior de los datos y de la cadena de consulta,
    4. URL

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

    Consumidor envía una petición HTTP al servidor web, después de la construcción de la firma (salida de hash hmac), la plantilla de solicitud HTTP:

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

    Ejemplo para OBTENER la solicitud:

    GET /webapi.hmac/api/values
    
    User-Agent: Fiddler    
    Host: localhost    
    Timestamp: Thursday, August 02, 2012 3:30:32 PM 
    Authentication: cuongle:LohrhqqoDy6PhLrHAXi7dUVACyJZilQtlDzNbLqzXlw=

    El mensaje de hash para obtener la firma:

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

    Ejemplo para la solicitud POST con la cadena de consulta (firma abajo no es correcto, sólo 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 de 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 de la cadena de consulta debe estar en orden, por lo que el código en el servidor obtiene de la cadena de consulta y datos de formulario para crear el mensaje correcto.

    Cuando HTTP petición llega al servidor de autenticación filtro de acción se lleva a cabo para analizar la petición para obtener información: HTTP verbo, timestamp, uri, y los datos del formulario de cadena de consulta, luego, en función de estos para construir firma (uso de hmac hash) con la clave secreta (contraseña con algoritmo hash) en el servidor.

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

    A continuación, el código del servidor compara la firma en la solicitud con la firma construido; si la igualdad, la autenticación se pasa, de lo contrario, no.

    El código para generar 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;
    }

    Así que, ¿cómo prevenir el ataque de reproducción?

    Agregar restricción para la marca de hora, algo así como:

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

    (servertime: el tiempo de las solicitudes al servidor)

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

    El código de demostración es puesto como aquí:
    https://github.com/cuongle/Hmac.WebApi

    • Gracias, que es útil código para mirar. ¿Tienes algún código de cliente demasiado?
    • Usted puede utilizar el filtro para la prueba, o escribir un HttpClient para crear solicitud.
    • En su AuthenticateAttribute en GitHub está validando la marca de tiempo Y la comprobación de si la firma anterior fue en la memoria caché para evitar ataques de repetición. Es necesario que tanto los cheques a suceder? ¿No sería más fiable para solo comprobar la marca manualmente (su primera sugerencia)?
    • sólo marca de hora no parece suficiente mucho, durante corto tiempo puede simular la solicitud y la envía al servidor, acabo de editar mi post, el uso de ambos sería el mejor.
    • Utilizando el usuario hash de la contraseña como clave secreta para firmar las solicitudes es una buena idea. A menos que usted va a asegurarse de que la clave secreta que se mantiene en secreto y nunca se transmiten a través del cable. Sin embargo, el hash de la contraseña necesidades de la sal, y, normalmente, que la sal debe ser al azar. En su caso, esta sal debe ser el mismo en ambos, el cliente y el servidor. También significa que todas las contraseñas de los usuarios se fragmentan utilizando la misma sal. ¿No es eso malo? O estoy totalmente equivocado?
    • Estás seguro de que esto funcione como debe? usted está hash de la marca de tiempo con el mensaje y la caché de ese mensaje. Esto significaría una firma distinta para cada solicitud que hace que su caché de la firma inútil.
    • parece que no entiendo tu punto de vista, la razón para utilizar la memoria Caché de aquí es para prevenir el ataque de retransmisión, nada más
    • Por favor explique cómo agregar usuario autenticado y realizar para permitir AllowAnonymous atributo está trabajando, actualmente no está funcionando.
    • Gran respuesta. Podría usted mostrar cómo un usuario puede generar su firma? Estoy atascado en esta parte.
    • Puede consultar [esta página] (jokecamp.wordpress.com/2012/10/21/…). Voy a actualizar esta fuente pronto
    • ¿Cómo funciona el consumidor obtenga el secreto? En su mensaje de que la contraseña con algoritmo hash generalmente es usado pero ¿cómo que trabajo, en donde la contraseña es salado y hash, que luego se almacena en una base de datos? Seguramente el consumidor no puede simplemente solicita la contraseña? Lo que me estoy perdiendo aquí?
    • Es como la forma de obtener la contraseña en el sitio web, a través del registro o el envío de un correo electrónico
    • ¿El uso de este para autenticar la aplicación a través de su API, o autenticar a un usuario real?
    • ¿Cuál es el punto de tu pregunta?
    • He publicado mi pregunta aquí stackoverflow.com/questions/22630447/…
    • Me gusta mucho esta solución, sin embargo ¿cómo se puede evitar que un intruso de entrometerse en el cliente de origen y la recuperación de la clave privada en orden a utilizar para crear la firma ??
    • No sé si la he entendido bien. Así, cuando se consume esa API, usted necesita para poner algo en la Cadena de consulta como la siguiente url : localhost/webapi.hmac/api/…» ? Analizar que la cadena de consulta en OnActionExecuting y establecer encabezados específicos?
    • Los valores de (nombre de usuario:firma) se agrega el encabezado de Autorización
    • Hola Cuong, por lo que en el contexto de una aplicación web MVC, después de que los usuarios han iniciado sesión (autenticado), has de enviar de vuelta el hash de la contraseña como un campo oculto en el formulario? O ¿cómo se puede obtener la clave secreta para firmar el mensaje en el lado del cliente?
    • Buena pregunta, supongo que no codificar la contraseña en el archivo javascript, sino que debe ser recuperada de la base de datos. También, js archivos debe ser record.
    • Se pone en el encabezado de solicitud HTTP
    • parece no estoy seguro de que estoy totalmente de entender tu pregunta, si su aplicación es ASP.NET MVC debe utilizar incorporado en el formulario de autenticación. Usted puede almacenar una clave secreta (password) para cada usuario en la base de datos
    • Mi pregunta era ¿cómo se puede calcular el based64 valor en el lado del cliente para que usted sería capaz de comparar ese valor con el que desde el servidor, ya que se necesita de una clave secreta, en este caso es el valor hash de la contraseña de un usuario? Así que supongo que cuando se dictó la página, incluido el valor de hash en un campo oculto?
    • en Javascript, puede utilizar cryptojs en: code.google.com/p/crypto-js para calcular el hash. Campo oculto va a almacenar el valor en el cuerpo de la solicitud, no en la cabecera. En este caso, usted debe llamar a la api de ajax en lugar de dejar que el formulario envía directamente al servidor
    • Así que esto significa que usted tiene que empujar el «secreto» en la forma de renderizar desde el servidor. Lo que significa que un usuario malintencionado será capaz de conseguir para llevar a cabo la firma del mensaje? ¿Cómo asegurarse de que los datos no son enviados por un usuario malintencionado?
    • eso es exactamente lo que necesito saber demasiado. Todavía no puedo comprender cómo prevenir que un atacante obtener el secreto !!!
    • bueno, después de recuperar el secreto de la base de datos, ¿cómo evitar que un atacante pueda conseguir este recuperado secreto ??
    • bien, ahora entiendo lo que tanto significaba, sus preguntas son para la autenticación HMAC en general, no específico para ASP.NET Web API, por lo que en el contexto de lado del cliente, necesitamos confiar en el usuario. Usted puede poner la clave secreta en cualquier lugar que el usuario «anónimo» no puede tener acceso, Js archivos, o incluso en la página html como largo como la clave secreta NO es traído por petición HTTP. Si pones una clave secreta en el formulario sólo para el usuario autenticado, ¿cómo puede un usuario malintencionado de acceso que forma?
    • por favor, ver mi comentario anterior
    • Mi enfoque simple sería la de pasar el ID de Usuario/PWD en el custome encabezado de solicitud HTTP y para implementar HTTPS/SSL para la WEB de la API
    • La solución que propuso funciona , pero no se puede evitar que el Hombre-en-el-ataque Medio , para que usted tenga para implementar HTTPS
    • Es esta una implementación de OAuth?
    • ¿qué acerca de csrf y ataque de xss en su apporach,un secreto oculto en el formulario no va a evitar estos ataques?

  2. 30

    Yo sugeriría comenzando con el más sencillo de soluciones de primera – tal vez simple Autenticación Básica de HTTP + HTTPS es suficiente en el escenario.

    Si no (por ejemplo, usted no puede utilizar https, o necesita más complejos de gestión de claves), usted puede tener una mirada en HMAC-soluciones sugeridas por otros. Un buen ejemplo de este tipo de API sería de Amazon S3 (http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html)

    Escribí un post en el blog acerca de HMAC la autenticación basada en ASP.NET Web API. Se describe tanto en la Web de la API de servicio Web y API de cliente y el código está disponible en bitbucket. http://www.piotrwalat.net/hmac-authentication-in-asp-net-web-api/

    Aquí es un post acerca de la Autenticación Básica en la API de Web: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/

    Recuerde que si usted va a proporcionar una API para 3 de las partes, también será más probable que sea responsable de la entrega de bibliotecas de cliente. La autenticación básica tiene una importante ventaja aquí es compatible con la mayoría de plataformas de programación de la caja. HMAC, por otro lado, no es que estandarizada y requieren de la implementación personalizada. Estos deben ser relativamente sencillo, pero todavía requieren de trabajo.

    PS. También hay una opción para utilizar HTTPS + certificados. http://www.piotrwalat.net/client-certificate-authentication-in-asp-net-web-api-and-windows-store-apps/

  3. 23

    ¿Has probado DevDefined.OAuth?

    La he utilizado para asegurar mi WebApi con 2 Patas de OAuth. También he probado con éxito con los clientes de PHP.

    Es muy fácil de añadir soporte para OAuth uso de esta biblioteca. He aquí cómo usted puede aplicar al proveedor para ASP.NET MVC Web API:

    1) Obtener el código fuente de DevDefined.OAuth: https://github.com/bittercoder/DevDefined.OAuth la nueva versión permite OAuthContextBuilder extensibilidad.

    2) Construir la biblioteca y hacer referencia a él en su Web API proyecto.

    3) Crear un contexto personalizado generador para apoyar la construcción de un contexto 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) Usar este tutorial para la creación de un proveedor de OAuth: http://code.google.com/p/devdefined-tools/wiki/OAuthProvider. En el último paso (el Acceso a Recursos Protegidos Ejemplo), usted puede utilizar este código en tu AuthorizationFilterAttribute atributo:

    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 de modo que yo no he probado el código anterior (excepto, por supuesto, la WebApiOAuthContextBuilder que estoy usando en mi proveedor) pero debería funcionar bien.

    • Gracias – voy a echar un vistazo a esto, a pesar de que por ahora me he enrollado mi propia HMAC-basado en la solución.
    • hola, dices que te ha hecho rodar su propia.. sólo había un par de preguntas si no te importa compartir. Estoy en una situación similar, donde tengo un relativamente pequeño MVC Web API. La API de controladores de sentarse junto a otro controlador/acciones que están bajo formas auth. La aplicación de OAuth parece una exageración cuando ya tengo un proveedor de pertenencia que podría utilizar y que sólo se necesita para asegurar un puñado de operaciones. Realmente quiero un auth acción que devuelve un testigo cifrado – a continuación, se utiliza el símbolo (token) en las llamadas posteriores? cualquier info de bienvenida antes de comprometerse en la implementación de una ya existente auth solución. gracias!
    • Majer – Cualquier posibilidad de que usted puede compartir cómo se ha implementado el proveedor en más detalle? Estoy teniendo algunos problemas para enviar las respuestas al cliente.
  4. 21

    Web API introdujo un Atributo [Authorize] para proporcionar seguridad. Esto puede ser establecido a nivel mundial (global.asx)

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

    O por controlador:

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

    De curso, el tipo de autenticación que pueden variar y es posible que desee llevar a cabo su propia autenticación, cuando esto ocurre, usted puede encontrar útil la herencia de Authorizate Atributo y de la ampliación a cumplir con sus requisitos:

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

    Y en el controlador:

    [DemoAuthorize]
    public class ValuesController : ApiController{

    Aquí hay un enlace en otros personalizado de la implementación de WebApi Autorizaciones:

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

    • Gracias por el ejemplo de @Dalorzo, pero tengo algunos problemas. He mirado el enlace adjunto, pero después de que las instrucciones no acaba de funcionar. También he encontrado información necesaria que falta. En primer lugar, al crear el nuevo proyecto, es el derecho a elegir a Cuentas de Usuario Individuales para la autenticación? O no me deja en ninguna autenticación. Yo también soy de no obtener la mencionada 302 error, pero estoy recibiendo un error 401. Por último, ¿cómo puedo pasar la información necesaria desde mi punto de vista al controlador? ¿Qué debe mi llamada ajax parece? Por cierto, estoy usando la autenticación de formularios para mi MVC vistas. Es eso un problema?
    • Se está trabajando de manera fantástica. Simplemente agradable para aprender y comenzar a trabajar en nuestros propios tokens de acceso.
    • Un pequeño comentario – tenga cuidado con AuthorizeAttribute, ya que hay dos clases diferentes con el mismo nombre, en diferentes espacios de nombres: 1. Sistema.Web.Mvc.AuthorizeAttribute -> para controladores MVC 2. Sistema.Web.Http.AuthorizeAttribute – > para WebApi.
  5. 6

    Si quieres asegurar tu API en un servidor de la moda (no la redirección a la página web de 2 patas de autenticación). Usted puede mirar en OAuth2 las Credenciales del Cliente Subvención protocolo.

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

    He desarrollado una biblioteca que puede ayudarle fácilmente a añadir este tipo de apoyo a su WebAPI. Se puede instalar como un paquete de NuGet:

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

    La biblioteca de los objetivos .NET Framework 4.5.

    Una vez que se agrega el paquete a su proyecto, se creará un archivo readme en el directorio raíz de tu proyecto. Usted puede mirar en ese archivo readme para ver cómo configurar/el uso de este paquete.

    Saludos!

    • Está compartiendo/proporcionar el código fuente para este framework de código abierto?
    • JFR: Primer Enlace está Roto, y el paquete de NuGet nunca fue actualizado
  6. 3

    en continuidad a @ Cuong Le de la respuesta , mi enfoque para prevenir el ataque de reproducción sería

    //Cifrar el Tiempo de Unix en el lado del Cliente usando la clave privada compartida(o contraseña del usuario)

    //Enviar como parte de encabezado de solicitud para el servidor(WEB API)

    //Descifrar el Tiempo de Unix en el Servidor(WEB API) el uso de la clave privada compartida(o contraseña del usuario)

    //Comprobar la diferencia de tiempo entre el Cliente del Tiempo de Unix y del Servidor de Tiempo de Unix, no debe ser mayor que x sec

    //si el ID de Usuario/Hash de la Contraseña son correctos y descifrado de UnixTime está dentro de x segundos de tiempo de servidor, a continuación, se trata de una solicitud válida

Dejar respuesta

Please enter your comment!
Please enter your name here