Como garantir um ASP.NET API Web [fechado]

quero construir um RESTful serviço web usando ASP.NET API Web que os desenvolvedores de terceiros usarão para acessar os dados da minha aplicação.

li bastante sobre OAuth e parece ser o padrão, mas encontrar uma boa amostra com documentação explicando como funciona (e isso realmente funciona!) parece ser incrivelmente difícil (especialmente para um novato para OAuth).

Existe uma amostra que realmente constrói e funciona e mostra como implementar isto?

Fiz o download de várias amostras.
  • DotNetOAuth-documentation is hopeless from a newbie perspective
  • Thinktecture-can't get it to build {[[15]}

eu também olhei para blogs sugerindo um esquema simples baseado em token (como Isso ) - Isso parece como reinventar a roda, mas tem a vantagem de ser conceitualmente bastante simples.

Parece que há muitas perguntas como esta, mas não há boas respostas.

O que foi? estão todos a fazer neste espaço?

Author: Hossein Narimani Rad, 2012-08-02

6 answers

Actualizar:

Coloquei aqui a minha outra resposta como usar a autenticação JWT para a API Web para qualquer pessoa interessada na JWT:

Autenticação JWT para Asp.Net Api Web


Conseguimos aplicar a autenticação HMAC para proteger a API Web, e funcionou bem. HMAC authentication uses a secret key for each consumer which both consumer and server both know to hmac hash a message, HMAC256 should be used. A maioria dos casos, senha hashed do consumidor é usado como uma chave secreta.

A mensagem normalmente é construída a partir de dados no pedido HTTP, ou mesmo dados personalizados que são adicionados ao cabeçalho HTTP, a mensagem pode incluir:

  1. hora do envio do pedido (UTC ou GMT)
  2. verbo HTTP: GET, POST, PUT, DELETE.
  3. publicar dados e texto de pesquisa,
  4. URL
Por baixo do capô, a autenticação HMAC seria:

O consumidor envia um pedido HTTP para o servidor web, após compilar a assinatura (saída do HMAC hash), o modelo do pedido HTTP:

User-Agent: {agent}   
Host: {host}   
Timestamp: {timestamp}
Authentication: {username}:{signature}
Exemplo para obter pedido:
GET /webapi.hmac/api/values

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

A mensagem para hash para obter a assinatura:

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

Exemplo para o pedido POST com o texto da consulta (a assinatura abaixo não está correcta, apenas um exemplo)

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

A mensagem para hash para obter a assinatura

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

Por favor, note que os dados do formulário e o texto da consulta devem estar em ordem, de modo que o código no servidor obter o texto da consulta e os dados do formulário para construir o mensagem correcta.

Quando o pedido de HTTP chega ao servidor, é implementado um filtro de acção de autenticação para processar o pedido para obter informações: verbo HTTP, timestamp, uri, formatação de dados e texto de consulta, depois com base nestes para compilar a assinatura (usar HMAC hash) com a chave secreta (senha hashed) no servidor.

A chave secreta é obtida a partir da base de dados com o nome de utilizador a pedido.

Então o código do servidor compara a assinatura no pedido com a assinatura comprada; se igual, a autenticação é passada, caso contrário, ela falhou.

O código para compilar a assinatura:

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;
}
Então, como evitar o ataque de repetição?

Adicionar uma restrição para a data, algo do género:

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

(servertime: hora do pedido a chegar ao servidor) {[[11]}

E, cache a assinatura do pedido em memória (use MemoryCache, deve manter no limite de tempo). Se o próximo pedido vier com a mesma assinatura com o pedido anterior, será rejeitado.

O código de demonstração é colocado como aqui: https://github.com/cuongle/Hmac.WebApi

 266
Author: Cuong Le, 2018-01-19 22:27:32

Eu sugeriria começar com as soluções mais simples primeiro-talvez a Autenticação Básica HTTP simples + HTTPS seja suficiente no seu cenário.

Caso contrário (por exemplo, não pode usar https, ou precisa de uma gestão de chaves mais complexa), pode dar uma vista de olhos nas soluções baseadas em HMAC, tal como sugerido por outros. Um bom exemplo de tal API seria Amazon S3 (http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html)

Eu escrevi um post no blog sobre a base HMAC. autenticação em ASP.NET API Web. Ele discute tanto o serviço de API Web e cliente de API Web e o código está disponível na bitbucket. http://www.piotrwalat.net/hmac-authentication-in-asp-net-web-api/

Aqui está um post sobre Autenticação Básica na API Web: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/

Lembre-se que se você vai fornecer uma API a terceiros, você também provavelmente será responsável por entregar Bibliotecas Clientes. A autenticação básica tem uma vantagem significativa aqui, pois é suportada na maioria das plataformas de programação fora da caixa. HMAC, por outro lado, não é que padronizado e vai exigir implementação personalizada. Estes devem ser relativamente simples, mas ainda requerem trabalho.

PS. Há também uma opção para usar certificados HTTPS +. http://www.piotrwalat.net/client-certificate-authentication-in-asp-net-web-api-and-windows-store-apps/

 29
Author: Piotr Walat, 2018-01-19 21:25:04
Já tentaste definir.OAuth? Usei-o para proteger o meu WebApi com OAuth de duas pernas. Eu também testei com sucesso com clientes PHP. É muito fácil adicionar suporte para o OAuth usando esta biblioteca. Aqui está como você pode implementar o provedor para ASP.NET API Web do MVC:

1) Obter o código fonte do DevDefined.OAuth: https://github.com/bittercoder/DevDefined.OAuth - a versão mais recente permite a extensibilidade OAuthContextBuilder.

2) construir a library and reference it in your Web API project.

3) Criar um construtor de contexto personalizado para suportar a construção de um 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) Use este tutorial para criar um fornecedor de OAuth: http://code.google.com/p/devdefined-tools/wiki/OAuthProvider . na última etapa (aceder ao exemplo de recursos protegidos) poderá usar este código no seu atributo 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()
            };
    }
}

Implementei o meu próprio fornecedor por isso não testei o código acima (excepto, é claro, o WebApiOAuthContextBuilder que estou a usar no meu fornecedor) mas deve funcionar bem.

 21
Author: Maksymilian Majer, 2013-07-26 13:02:30

A API Web introduziu um atributo [Authorize] para fornecer segurança. Isto pode ser definido globalmente (global.asx)

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

Ou por controlador:

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

É claro que o seu tipo de autenticação pode variar e você pode querer realizar a sua própria autenticação, quando isso ocorrer você pode achar útil herdar do atributo autorizate e estendê-lo para atender aos seus 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;
        }
    }
}

E no seu controlador:

[DemoAuthorize]
public class ValuesController : ApiController{

Aqui está uma ligação sobre outra implementação personalizada para WebApi Autorizações:

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

 18
Author: Dalorzo, 2015-02-28 20:37:06

Se quiser proteger a sua API de forma servidor a servidor (sem redireccionamento para o website para autenticação com 2 legged). Você pode olhar para o OAuth2 Credencies Grant protocol.

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

Desenvolvi uma biblioteca que pode ajudá-lo a adicionar este tipo de suporte à sua WebAPI. Você pode instalá-lo como um NuGet embalagem:

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

The library targets. NET Framework 4.5.

Assim que adicionar o pacote ao seu projecto, ele irá criar um ficheiro readme na raiz do seu projecto. Você pode olhar para esse arquivo readme para ver como configurar/usar este pacote.

Saúde!
 5
Author: Varun Chatterji, 2013-05-20 03:47:39
Na continuação da resposta de @ Cuong Le, a minha abordagem para evitar o ataque de repetição seria {[[2]}

/ / encriptar a hora Unix do lado do cliente usando a chave privada partilhada (ou a senha do Utilizador)

/ / Enviar como parte do cabeçalho do pedido para o servidor(API WEB)

/ / descodificar a hora Unix no servidor (API WEB) usando a chave privada partilhada (ou a senha do Utilizador)

/ / Verifique a diferença horária entre o tempo Unix do cliente e o tempo Unix do servidor, não deve ser maior que x sec

// se a senha do ID do utilizador / Hash estiver correcta e o UnixTime descodificado estiver dentro de x segundos do tempo do servidor, então é um pedido válido

 2
Author: refactor, 2015-10-16 11:45:44