Certificação Microsoft 70-487: Objetivo 4.3 – Secure a Web API

Olá pessoal!
O objetivo 4.3 da certificação 70-487, Secure a Web API, trata de forma bem completa sobre as diferentes formas de segurança e autenticação disponíveis para serviços Web API.

Esse objetivo cobre como 1) autenticar e autorizar usuários, 2) implementar a autenticação HTTP Basic com SSL, 3) implementar Windows Authentication, 4) prevenir cross-site request forgery, 5) habilitar requests cross domain e 6) implementar e extender authorization filters.

Autenticar E Autorizar Usuários

Essa seção apenas explica os conceitos de autenticação e autorização.

Autenticação é o processo de identificação do usuário, geralmente por um nome de usuário e senha. O framework da Web API tem uma série de ferramentas para implementação desse processo, o que vamos ver logo mais.

Autorização é o processo que define quais ações o usuário pode fazer no sistema. Por exemplo, você pode permitir que usuários anônimos façam apenas consultas, usuários autenticados façam inserções e usuários administrativos façam atualizações.

Implementando Autenticação HTTP Basic

HTTP Basic é a forma mais simples e fácil de implementar um mecanismo de autenticação. É um formato conhecido e maduro, suportado pela maioria dos browsers e nativamente suportado pelo IIS. A principal desvantagem é que as credenciais são transmitidas em plain text nos headers do request, tornando-se vulnerável a interceptores. Também não há como fazer logout explicitamente a não ser que a sessão do browser se encerre.

O fluxo com HTTP Basic é assim:

Um client faz um request. Se o serviço necessitar autenticação, o server retorna o status code 401 (unauthorized) com o seguinte header: WWW-Authenticate: Basic realm="Realm Name". Esse header especifica que o server suporta a autenticação Basic no domínio especificado (realm).

O client, ao receber um response 401, faz outro request com o header de autenticação. A construção do header começa com o nome de usuário e a senha concatenados por um dois-pontos. Por exemplo, caso o nome de usuário seja JohnQPublic e a senha !*MyPa55w0rd*!, o valor seria JohnQPublic:!*MyPa55w0rd*!. Em seguida esse valor seria encodado por Base64, resultando na string Sm9oblFQdWJsaWM6ISpNeVBhNTV3MHJkKiE=. Por fim, a palavra Basic é adicionada antes dessa string e enviada no header Authorization: Authorization: Basic Sm9oblFQdWJsaWM6ISpNeVBhNTV3MHJkKiE=.

Um ponto importante é que o nome de usuário e senha, apesar de encodados em Base64, não estão encriptados. Qualquer interceptor que tiver acesso aos headers do request vai conseguir decifrar dados sensíveis do usuário facilmente. Por isso, a autenticação Basic não é segura a não ser que feita por HTTPS, onde todo o request é criptografado. Para o exame (e para a vida real), lembre-se: nunca use autenticação Basic sem SSL.

Habilitando SSL

Como a autenticação Basic não deve ser utilizada sem SSL, você deve saber como forçar uma conexão HTTPS para Web API.

Se você viu como implementar Action Filters no objetivo anterior, vai reconhecer como vamos fazer isso agora. De forma simples, basta implementar a classe AuthorizationFilterAttribute e sobrescrever o método OnAuthorization com uma validação. Segue um exemplo:

Depois disso, você deve decorar os controllers desejados com esse atributo ou registrá-lo globalmente no HttpConfiguration.

Não sei se cairá no exame, mas saiba que há uma ferramenta chamada MakeCert.exe para gerar certificados SSL para testes.

Implementando Windows Authentication

Windows Authentication é um processo onde os usuários podem se autenticar pelo seu login no Windows, Kerberos ou NTLM. É ideal para aplicações intranet que serão acessadas somente de dentro do mesmo domínio, em uma empresa por exemplo. Obviamente, não é recomendada para cenários onde usuários “anônimos” vão acessar sua API.

Por ser nativamente suportada pelo IIS, implementar essa autenticação é bem simples. Basta configurar a propriedade Mode do elemento Authentication para “Windows” no web.config, como a seguir:

<system.web>
<authentication mode="Windows" />
</system.web>

Se o client for diretamente um browser, este precisa dar suporte ao esquema de autenticação Negotiation. Não há muito o que se preocupar pois todos os browsers populares têm esse suporte.
Caso seja uma aplicação .NET, a classe HttpClient deve ser usada para fazer o request (como sempre). A única modificação é que ela deve ser instanciada com um HttpClientHandler, que pode ser obtido da seguinte maneira:

HttpClientHandler clientHandler = new HttpClientHandler
{
UseDefaultCredentials = true
};

Prevenindo Cross-Site Request Forgery

Cross-Site Request Forgery (CSRF ou XSRF) é uma forma bastante utilizada por atacantes para acessarem recursos disponíveis para usuários autenticados.

Vamos supor que você está logado no portal do Azure e, no mesmo browser, recebe um e-mail dizendo que você tem desconto de 50% para novos serviços no Azure. Ao clicar no link do e-mail, você é direcionado para a página https://manage.windowsazure.com/api/DeleteAllData sem perceber. É claro que essa página não existe no portal, mas se existisse, você estaria em sérios problemas.

Resumidamente, um ataque XSRF acontece quando você está autenticado em algum sistema e um atacante se aproveita disso para executar alguma ação. Seu sistema está vulnerável a ataques XSFR se você usa um mecanismo de autenticação que mantém o usuário logado (baseado em cookies) ou quando você permite requests cross domain. A autenticação Basic também é vulnerável pois o browser normalmente armazena seu token de autenticação para evitar que o usuário digite o usuário e senha a cada request.

Para se proteger de ataques XSRF, há uma solução pronta em MVC e que os mesmos princípios podem ser aplicados na Web API.

Usando o método Html.AntiForgeryToken na view e o atributo ValidateAntiForgeryToken na action, o MVC primeiro renderiza um input hidden na tela assim: <input name="__RequestVerificationToken" type="hidden" value="saTFWpkKN0BYazFtN6c4YbZAmsEwG0srqlUqqloi/fVgeV2ciIFVmelvzwRZpArs" />. Esse valor é gerado randomicamente e também é colocado num cookie passado para o usuário. Ao fazer um post, por exemplo, o MVC valida se o valor do cookie e o do form data são iguais. Isso é seguro pois o atacante não vai saber o valor do cookie para forjar um request válido.

Para Web API, como dito, o princípio pode ser aplicado. Você manda um valor no cookie e no HTML e depois garante que eles são o mesmo. Você pode mandar o token antiforgery usando o mesmo método Html.AntiForgeryToken.

Para validar o token na Web API, você deve obter o token do cookie; obter do form data; passar ambos para o método AntiForgery.Validate e retornar um response Unauthorized caso a validação falhe. O código ficaria assim:

Habilitando Requests Cross Domain

Por padrão, os browsers proíbem requests cross domain, ou seja, para domínios diferentes do atual. Por exemplo, se você está no site http://www.contoso.com, você não pode fazer uma requisição AJAX para http://www.someotherwebsite.com. Você também não pode fazer requests para o seu domínio com outro protocolo (HTTP ao invés de HTTPS) ou para outra porta. Isso existe para evitar ataques XSRF, já que a maioria das autenticações funcionam por cookie.

Porém você pode querer que sua API seja consumida por vários clients, e isso se tornaria um problema. Isso se chama CORS (cross origin resource sharing), e você pode habilitá-lo facilmente no arquivo web.config:

Isso adiciona um header nos responses, indicando que CORS está habilitado. Com isso, qualquer site pode fazer requisições para sua API, o que pode não ser desejado.

Para resolver esse problema, a Microsoft criou um pacote NuGet chamado Microsoft.AspNet.WebApi.Cors. Após a instalação, você tem um método de extensão no objeto HttpConfiguration: config.EnableCors();.

Chamando esse método no método WebApiConfig.Register, você habilita o suporte a CORS na sua aplicação. Agora você pode adicionar o atributo EnableCors para controllers e actions assim:

Nesse caso, você habilitou CORS somente para esse método na URL especificada em origins.

Implementando E Extendendo Authorization Filters

A Web API oferece o atributo AuthorizeAttribute que por padrão garante que os usuários acessando esse controller ou action estão autenticados e têm as roles necessárias, se especificadas.

Você pode estender a classe AuthorizeAttribute e sobrescrever o método IsAuthorized para implementar sua própria lógica. O exemplo a seguir faz uma implementação de black list, ou seja, usuários com as roles especificadas não podem acessar a action:

Você também pode estender a classe AuthorizationFilterAttribute ou implementar a interface IAuthorizationFilter para realizar alguma lógica customizada. As diferenças entre elas são as seguintes:

  • AuthorizeAttribute: usada para implementar lógica de autorização baseada no usuário e nas roles;
  • AuthorizationFilterAttribute: usada para implementar lógica síncrona de autorização que não está baseada no usuário e nas roles (por exemplo, forçar HTTPS);
  • IAuthorizationFilter: usada para implementar lógica assíncrona de autorização que não está baseada no usuário e nas roles.

Obrigado pela leitura de mais um objetivo!
O próximo post será sobre o objetivo 4.4, Host and manage a Web API.

Até lá!