Diretrizes de mitigação de ameaças para renderização estática do lado do servidor do ASP.NET Core Blazor

Este artigo explica as considerações de segurança que os desenvolvedores devem considerar ao desenvolverem os Blazor Aplicativos Web com renderização estática do lado do servidor.

Blazor combina três modelos diferentes em um para escrever os aplicativos Web interativos. Renderização tradicional do lado do servidor, que é um modelo de solicitação/resposta baseado no HTTP. Renderização interativa do lado do servidor, que é um modelo de renderização baseado no SignalR. Por fim, a renderização do lado do cliente, que é um modelo de renderização baseado no WebAssembly.

Todas as considerações gerais de segurança definidas nos modos de renderização interativos se aplicam aos Blazor Aplicativos Web quando houver componentes interativos renderizando em um dos modos de renderização com suporte. As seções a seguir explicam as considerações de segurança específicas para renderização não interativa do lado do servidor nos Blazor Aplicativos Web e os aspectos específicos que se aplicam quando os modos de renderização interagem entre si.

Considerações gerais sobre a renderização do lado do servidor

O modelo de renderização do lado do servidor (SSR) é baseado no modelo tradicional de solicitação/resposta do HTTP. Como tal, existem áreas comuns de preocupação entre o SSR e o HTTP de solicitação/resposta. As considerações gerais de segurança e ameaças específicas devem ser atenuadas com êxito. A estrutura fornece mecanismos internos para gerenciar algumas dessas ameaças, mas outras ameaças são específicas do código do aplicativo e devem ser tratadas pelo aplicativo. Essas ameaças podem ser categorizadas da seguinte maneira:

  • Autenticação e autorização: o aplicativo deve garantir que o usuário esteja autenticado e autorizado a acessar o aplicativo e os recursos que ele expõe. A estrutura fornece mecanismos internos para autenticação e autorização, mas o aplicativo deve garantir que os mecanismos sejam configurados e usados corretamente. Os mecanismos internos de autenticação e autorização são abordados no Blazor nó de segurança do Servidor da documentação e na Segurança e nó da IdentityDocumentação do ASP.NET Core, portanto, eles não serão abordados aqui.

  • Validação e limpeza de entradas: todas as entradas que chegam de um cliente devem ser validadas e limpas antes do uso. Caso contrário, o aplicativo poderá ser exposto a ataques, como injeção de SQL, script entre sites, falsificação de solicitação entre sites, redirecionamento aberto e outras formas de ataques. A entrada pode vir de qualquer lugar na solicitação.

  • Gerenciamento de sessão: gerenciar corretamente as sessões do usuário é fundamental para garantir que o aplicativo não seja exposto a ataques, como correção de sessão, sequestro de sessão e outros ataques. As informações armazenadas na sessão devem ser devidamente protegidas e criptografadas, e o código do aplicativo deve impedir que um usuário mal-intencionado adivinhe ou manipule sessões.

  • Tratamento de erros e registro em log: o aplicativo deve garantir que os erros sejam manipulados e registrados corretamente. Caso contrário, o aplicativo poderá ser exposto a ataques, como a divulgação de informações. Isso pode acontecer quando o aplicativo retorna informações confidenciais na resposta ou quando o aplicativo retorna mensagens de erro detalhadas com dados que podem ser usados para atacar o aplicativo.

  • Proteção de dados: os dados confidenciais devem ser devidamente protegidos, o que inclui a lógica do aplicativo quando executado no WebAssembly, uma vez que podem ser facilmente submetidos a engenharia reversa.

  • Negação de serviço: o aplicativo deve garantir que ele não seja exposto a ataques, como a negação de serviço. Por exemplo, isso acontece quando o aplicativo não está devidamente protegido contra ataques de força bruta ou quando uma ação pode fazer com que o aplicativo consuma muitos recursos.

Validação e limpeza da entrada

Todas as entradas que chegam do cliente devem ser consideradas não confiáveis, a menos que suas informações tenham sido geradas e protegidas no servidor, como um token CSRF, uma autenticação cookie, um identificador de sessão ou qualquer outra carga protegida com criptografia autenticada.

Normalmente, a entrada está disponível para o aplicativo através de um processo de associação, por exemplo, através do atributo [SupplyParameterFromQuery] ou [SupplyParameterFromForm]. Antes de processar essa entrada, o aplicativo deve verificar se os dados são válidos. Por exemplo, o aplicativo deve confirmar que não houve erros de associação ao mapear os dados do formulário para uma propriedade de componente. Caso contrário, o aplicativo poderá processar dados inválidos.

Se a entrada for usada para executar um redirecionamento, o aplicativo deverá verificar se a entrada é válida e se não está apontando para um domínio considerado inválido ou para um subcaminho inválido no caminho base do aplicativo. Caso contrário, o aplicativo pode ser exposto a ataques de redirecionamento abertos, em que um invasor pode criar um link que redireciona o usuário para um site mal-intencionado.

Se a entrada for usada para executar uma consulta ao banco de dados, o aplicativo deverá confirmar se a entrada é válida e se não está expondo o aplicativo a ataques de injeção de SQL. Caso contrário, um invasor poderá criar uma consulta mal-intencionada que pode ser usada para extrair informações do banco de dados ou para modificá-lo.

Os dados que podem ter vindo da entrada do usuário também devem ser limpos antes de serem incluídos em uma resposta. Por exemplo, a entrada pode conter HTML ou JavaScript que podem ser usados para executar ataques de script entre sites, que podem ser usados para extrair informações do usuário ou para executar ações em nome do usuário.

A estrutura fornece os seguintes mecanismos para ajudar na validação e limpeza das entradas:

  • Todos os dados do formulário associados são validados para correção básica. Se uma entrada não puder ser analisada, o processo de associação relatará um erro que o aplicativo pode descobrir antes de executar qualquer ação com os dados. O componente EditForm interno leva isso em conta antes de invocar o retorno de chamada do formulário OnValidSubmit. Blazor evita a execução do retorno de chamada se houver um ou mais erros de associação.
  • A estrutura usa um token antifalsificação para proteger contra ataques de falsificação de solicitação entre sites. Para obter mais informações, confira Autenticação e autorização do Blazor no ASP.NET Core e Visão geral dos formulários do Blazor no ASP.NET Core.

Todas as entradas e permissões devem ser validadas no servidor no momento da execução de uma determinada ação para garantir que os dados sejam válidos e precisos naquele momento e que o usuário tenha permissão para executar a ação. Essa abordagem é consistente com as diretrizes de segurança fornecidas para renderização interativa do lado do servidor.

Gerenciamento da sessão

O gerenciamento de sessão é tratado pela estrutura. A estrutura usa uma sessão cookie para identificar a sessão do usuário. A sessão cookie é protegida usando as APIs de Proteção de Dados do ASP.NET Core. A sessão cookie não é acessível ao código JavaScript em execução no navegador e não pode ser facilmente adivinhada ou manipulada por um usuário.

Quanto a outros dados de sessão, como dados armazenados em serviços, os dados de sessão devem ser armazenados em serviços com escopo, pois os serviços com escopo são exclusivos de uma determinada sessão de usuário, ao contrário dos serviços de banco de dados individual, que são compartilhados por todas as sessões de usuário em uma determinada instância de processo.

Quando se trata de SSR, não há muita diferença entre serviços com escopo e transitórios na maioria dos casos, pois a vida útil do serviço é limitada a uma única solicitação. Há uma diferença em dois cenários:

  • Se o serviço for injetado em mais de um local ou em momentos diferentes durante a solicitação.
  • Se o serviço puder ser usado em um contexto de servidor interativo, em que ele sobrevive a várias renderizações e é fundamental que o serviço tenha o escopo da sessão do usuário.

Registro em log e tratamento de erros

A estrutura fornece log interno para o aplicativo no nível da estrutura. A estrutura registra eventos importantes, como quando o token antifalsificação de um formulário falha na validação, quando um componente raiz começa a ser renderizado e quando uma ação é despachada. O aplicativo é responsável por registrar em log quaisquer outros eventos que possam ser importantes para serem registrados.

A estrutura fornece tratamento de erros interno para o aplicativo no nível da estrutura. A estrutura lida com erros que ocorrem durante a renderização de um componente e usa o mecanismo de limite de erro para exibir uma mensagem de erro amigável ou permite que o erro chegue ao middleware de tratamento de exceções, o qual está configurado para renderizar a página de erro.

Os erros que ocorrem durante a renderização de streaming após a resposta ter começado a ser enviada ao cliente são exibidos na resposta final como uma mensagem de erro genérica. Os detalhes sobre a causa do erro são incluídos apenas durante o desenvolvimento.

Proteção de dados

A estrutura oferece mecanismos para proteger informações confidenciais para uma determinada sessão de usuário e garante que os componentes integrados usem esses mecanismos para proteger informações confidenciais, como proteger a identidade do usuário ao usar a autenticação cookie. Fora dos cenários tratados pela estrutura, o código do desenvolvedor é responsável por proteger outras informações específicas do aplicativo. A maneira mais comum de fazer isso é pelas APIs de Proteção de Dados do ASP.NET Core ou qualquer outra forma de criptografia. Como regra geral, o aplicativo é responsável por:

  • Certificar-se de que um usuário não pode inspecionar ou modificar as informações privadas de outro usuário.
  • Certificar-se de que um usuário não pode modificar os dados de usuário de outro usuário, como um identificador interno.

No que diz respeito à proteção de dados, você deve entender claramente onde o código está sendo executado. Para a renderização estática do lado do servidor (SSR estático) e a renderização interativa do lado do servidor (SSR interativa), o código é armazenado no servidor e nunca chega ao cliente. No modo de renderização WebAssembly Interativo, o código do aplicativo sempre chega ao cliente, o que significa que todas as informações confidenciais armazenadas no código do aplicativo estão disponíveis para qualquer pessoa com acesso ao aplicativo. A ofuscação e outras técnicas semelhantes para "proteger" o código não são eficazes. Uma vez que o código chega ao cliente, ele pode ser revertido para extrair as informações confidenciais.

Negação de serviço

No nível do servidor, a estrutura fornece limites nos parâmetros de solicitação/resposta, como o tamanho máximo da solicitação e o tamanho do cabeçalho. Em relação ao código do aplicativo, o sistema de mapeamento de formulários do Blazor define limites semelhantes aos definidos pelo sistema de associação de modelos do MVC:

  • Limite do número máximo de erros.
  • Limite da profundidade máxima de recursão para o fichário.
  • Limite do número máximo de elementos associados em uma coleção.

Além disso, há limites definidos para o formulário, como o tamanho máximo da chave de formulário, tamanho do valor e o número máximo de entradas.

Em geral, o aplicativo deve avaliar quando há a possibilidade de uma solicitação acionar uma quantidade assimétrica de trabalho pelo servidor. Exemplos disso incluem quando o usuário envia uma solicitação parametrizada por N e o servidor executa uma operação em resposta que é N vezes mais cara, onde N é um parâmetro que um usuário controla e pode crescer indefinidamente. Normalmente, o aplicativo deve impor um limite ao N máximo que está disposto a processar ou garantir que qualquer operação seja menor, igual ou mais cara do que a solicitação por um fator constante.

Esse aspecto tem mais a ver com a diferença de crescimento entre o trabalho que o cliente executa e o trabalho que o servidor executa do que com uma comparação específica de 1→N. Por exemplo, um cliente pode enviar um item de trabalho (inserir elementos em uma lista) que leva N unidades de tempo para ser executado, mas o servidor precisa de N^2^ para processar (porque pode estar fazendo algo muito ingênuo). É a diferença entre N e N^2^ que importa.

Dessa forma, há um limite de quanto trabalho o servidor deve estar disposto a realizar o que é específico para o aplicativo. Esse aspecto se aplica às cargas de trabalho do lado do servidor, uma vez que os recursos estão no servidor, mas não se aplica necessariamente às cargas de trabalho do WebAssembly no cliente, na maioria dos casos.

O outro aspecto importante é que isso não é reservado apenas ao tempo de CPU. Ele também se aplica a quaisquer recursos, como memória, rede e espaço em disco.

Nas cargas de trabalho do WebAssembly, geralmente há pouca preocupação com a quantidade de trabalho que o cliente executa, uma vez que o cliente normalmente é limitado pelos recursos disponíveis no cliente. Contudo, há alguns cenários em que o cliente pode ser afetado, se, por exemplo, um aplicativo exibir dados de outros usuários e um usuário puder adicionar dados ao sistema que forcem os clientes que exibem os dados a executarem uma quantidade de trabalho que não seja proporcional à quantidade de dados adicionados pelo usuário.

  • Certifique-se de que o usuário esteja autenticado e autorizado a acessar o aplicativo e os recursos que ele expõe.
  • Valide e limpe todas as entradas provenientes de um cliente antes de usá-las.
  • Gerencie corretamente as sessões do usuário para garantir que o estado não seja compartilhado por engano entre os usuários.
  • Manipule e registre os erros corretamente para evitar a exposição de informações confidenciais.
  • Registre os eventos importantes no aplicativo para identificar possíveis problemas e auditar ações as executadas pelos usuários.
  • Proteja as informações confidenciais usando as APIs de Proteção de Dados do ASP.NET Core ou um dos componentes disponíveis (Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage, PersistentComponentState).
  • Certifique-se de que o aplicativo compreenda os recursos que podem ser consumidos por uma determinada solicitação e tenha limites para evitar ataques de negação de serviço.