Diretrizes de mitigação de ameaças para a renderização interativa do Blazor do ASP.NET Core do lado do servidor

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Este artigo explica como mitigar as ameaças de segurança no Blazor interativo do lado do servidor.

Os aplicativos adotam um modelo de processamento de dados com estado, em que o servidor e o cliente mantêm uma relação de longa duração. O estado persistente é mantido por um circuito, que pode abranger conexões que também são possivelmente de longa duração.

Quando um usuário visita um site , o servidor cria um circuito na memória do servidor. O circuito indica ao navegador qual conteúdo deve ser renderizado e responde a eventos, como quando o usuário seleciona um botão na interface do usuário. Para executar essas ações, um circuito invoca funções JavaScript no navegador do usuário e métodos do .NET no servidor. Essa interação bidirecional baseada em JavaScript é conhecida como Interoperabilidade do JavaScript (interoperabilidade JS).

Como a interoperabilidade JS ocorre pela Internet e o cliente usa um navegador remoto, os aplicativos compartilham a maioria das preocupações de segurança do aplicativo Web. Este tópico descreve ameaças comuns aos aplicativos Blazor do servidor e fornece diretrizes de mitigação de ameaças com foco em aplicativos para a Internet.

Em ambientes restritos, como dentro de redes corporativas ou intranets, algumas das diretrizes de mitigação:

  • Não se aplicam ao ambiente restrito.
  • Não valem o custo de implementação porque o risco de segurança é baixo em um ambiente restrito.

Componentes interativos do servidor com a compactação WebSocket habilitada

A compactação pode expor o aplicativo a ataques de canal lateral em relação a criptografia TLS da conexão, como ataques CRIME e BREACH. Esses tipos de ataques requerem que o invasor cibernético:

  • Force um navegador a enviar solicitações com um conteúdo controlado pelo invasor cibernético para um site vulnerável por meio de postagem de formulário entre sites ou pela incorporação do site dentro de um iframe de outro site.
  • Observar o comprimento da resposta compactada e criptografada na rede.

Para que o aplicativo seja vulnerável, ele deve refletir o payload do invasor cibernético na resposta, por exemplo, escrevendo o caminho ou a cadeia de caracteres de consulta na resposta. Usando o comprimento da resposta, o invasor cibernético pode "adivinhar" qualquer informação na resposta, ignorando a criptografia da conexão.

De modo geral, os aplicativos Blazor podem habilitar a compactação pela conexão WebSocket com as medidas de segurança adequadas:

  • O aplicativo pode ser vulnerável quando ele recebe conteúdo da solicitação (por exemplo, o caminho ou a cadeia de caracteres de consulta) que pode ser influenciado por um invasor cibernético e o reproduz no HTML da página ou de outra forma o torna parte da resposta.

  • Blazor aplica as seguintes medidas de segurança automaticamente:

    • Quando a compactação é configurada, o Blazor bloqueia automaticamente a incorporação do aplicativo em um iframe, o que impede a renderização da resposta inicial (não compactada) do servidor e impede que a conexão WebSocket seja iniciada.

    • A restrição de inserção do aplicativo em um iframe pode ser relaxada. No entanto, relaxar a restrição expõe o aplicativo a um ataque se o documento incorporado ficar comprometido por meio de uma vulnerabilidade de cross-site scripting, pois isso dá ao invasor cibernético uma maneira de executar o ataque.

  • Normalmente, para que esse tipo de ataque ocorra, o aplicativo deve reproduzir repetidamente o conteúdo nas respostas para que o invasor cibernético possa adivinhar a resposta. Dada a forma como o Blazor renderiza (ele renderiza uma vez e, em seguida, produz diferenças do conteúdo somente para os elementos que foram alterados), é difícil para um invasor cibernético fazer isso. Entretanto, não é impossível para um invasor cibernético; portanto, deve-se tomar cuidado para evitar renderizar informações confidenciais com informações externas que possam ser manipuladas por um invasor cibernético. Alguns exemplos disso são:

    • Renderizar as informações de identificação pessoal (PII) na página ao mesmo tempo em que renderiza dados do banco de dados que foram adicionados por outro usuário.

    • Renderizar informações de PII na página ao mesmo tempo que os dados provenientes de outro usuário por meio da interoperabilidade JS ou de um serviço singleton local no servidor.

Em geral, recomendamos que você evite renderizar componentes que contenham informações confidenciais juntamente com componentes que possam renderizar dados de fontes não confiáveis como parte do mesmo lote de renderização. As fontes não confiáveis incluem parâmetros de rota, cadeias de caracteres de consulta, dados de interoperação com JS e qualquer outra fonte de dados que um usuário de terceiros possa controlar (bancos de dados, serviços externos).

Estado compartilhado

Os aplicativos Blazor do lado do servidor estão localizados na memória do servidor, e várias sessões de aplicativo são hospedadas no mesmo processo. Para cada sessão de aplicativo, Blazor inicia um circuito com seu próprio escopo de contêiner de injeção de dependência. Portanto, os serviços com escopo são exclusivos por sessão Blazor.

Aviso

Não recomendamos que os aplicativos no mesmo estado de compartilhamento de servidor usem serviços singleton, a menos que sejam tomados cuidados extremos, pois isso pode introduzir vulnerabilidades de segurança, como o vazamento do estado do usuário entre circuitos.

Você pode usar serviços singleton com estado em aplicativos Blazor, se forem criados especificamente para isso. Por exemplo, o uso de um cache de memória singleton é aceitável porque um cache de memória requer uma chave para acessar determinada entrada. Supondo que os usuários não tenham controle sobre as chaves de cache usadas com o cache, o estado armazenado no cache não vazará entre circuitos.

Para obter diretrizes gerais sobre o gerenciamento de estado, confira Gerenciamento de estado Blazor do ASP.NET Core.

IHttpContextAccessor/HttpContext em componentes Razor

IHttpContextAccessor deve ser evitado com a renderização interativa porque não há um HttpContext disponível válido.

IHttpContextAccessor pode ser usado para componentes que são renderizados estaticamente no servidor. No entanto, recomendamos evitá-lo, se possível.

HttpContext pode ser usado como um parâmetro em cascata apenas em componentes raiz renderizados estaticamente para tarefas gerais, como inspecionar e modificar cabeçalhos ou outras propriedades no componente App (Components/App.razor). O valor é sempre null para renderização interativa.

[CascadingParameter]
public HttpContext? HttpContext { get; set; }

Para cenários em que o componente HttpContext é necessário em componentes interativos, recomendamos fluir os dados por meio do estado de componente persistente do servidor. Para obter mais informações, confira Cenários de segurança adicionais Blazor do ASP.NET Core do lado do servidor.

Não use IHttpContextAccessor/HttpContext direta ou indiretamente nos componentes Razor dos aplicativos Blazor do lado do servidor. Os aplicativos Blazor são executados fora do contexto do pipeline ASP.NET Core. Não há garantia de que o HttpContext esteja disponível no IHttpContextAccessor e não há garantia de que o HttpContext mantenha o contexto que iniciou o aplicativo Blazor.

A abordagem recomendada para passar o estado da solicitação para o aplicativo Blazor é por meio de parâmetros de componente raiz durante a renderização inicial do aplicativo. Como alternativa, o aplicativo pode copiar os dados para um serviço com escopo no evento de ciclo de vida de inicialização do componente raiz para uso em todo o aplicativo. Para obter mais informações, confira Cenários de segurança adicionais Blazor do ASP.NET Core do lado do servidor.

Um aspecto crítico da segurança do lado do servidor Blazor é que o usuário anexado a um determinado circuito pode ser atualizado em algum momento após o circuito Blazor ser estabelecido, mas o IHttpContextAccessor não é atualizado. Para obter mais informações sobre como lidar com essa situação usando serviços personalizados, confira Outros cenários de segurança do ASP.NET Core Blazor do servidor.

Esgotamento de recursos

O esgotamento de recursos pode ocorrer quando um cliente interage com o servidor e faz com que o servidor consuma recursos excessivos. O consumo excessivo de recursos afeta principalmente o seguinte:

Ataques de negação de serviço (DoS) geralmente buscam esgotar os recursos de um aplicativo ou servidor. No entanto, o esgotamento de recursos não é necessariamente o resultado de um ataque ao sistema. Por exemplo, recursos finitos podem ser esgotados devido à alta demanda do usuário. O DoS é abordado ainda mais na seção DoS.

Recursos externos à estrutura Blazor, como bancos de dados e identificadores de arquivo (usados para ler e gravar arquivos), também podem ter esgotamento de recursos. Para obter mais informações, confira Melhores Práticas do ASP.NET Core.

CPU

O esgotamento da CPU pode ocorrer quando um ou mais clientes forçam o servidor a executar um trabalho de uso intensivo de CPU.

Por exemplo, considere um aplicativo que calcula um número de Fibonacci. Um número Fibonnacci é produzido a partir de uma sequência de Fibonnacci, onde cada número na sequência é a soma dos dois números anteriores. O trabalho necessário para acessar a resposta depende do tamanho da sequência e do valor inicial. Se o aplicativo não colocar limites na solicitação de um cliente, os cálculos com uso intensivo de CPU poderão dominar o tempo da CPU e diminuir o desempenho de outras tarefas. O consumo excessivo de recursos é uma preocupação de segurança que afeta a disponibilidade.

O esgotamento da CPU é uma preocupação de todos os aplicativos voltados para o público. Em aplicativos da Web comuns, as solicitações e conexões atingem o tempo limite como proteção, mas os aplicativos Blazor não fornecem as mesmas proteções. Os aplicativos Blazor devem incluir verificações e limites apropriados, antes de executar um trabalho com possível uso intensivo de CPU.

Memória

O esgotamento de memória pode ocorrer quando um ou mais clientes forçam o servidor a consumir um grande volume de memória.

Por exemplo, considere um aplicativo com um componente que aceita e exibe uma lista de itens. Se o aplicativo Blazor não colocar limites no número de itens permitidos ou no número de itens renderizados novamente para o cliente, o processamento e a renderização com uso intensivo de memória poderão dominar a memória do servidor a ponto prejudicar o desempenho do servidor. O servidor pode travar ou ficar lento a ponto de parecer que falhou.

Considere o cenário a seguir para manter e exibir uma lista de itens que pertencem a um possível cenário de esgotamento de memória no servidor:

  • Os itens em uma propriedade ou campo List<T> usam a memória do servidor. Se o aplicativo permitir que a lista de itens aumente sem associação, existe o risco de o servidor ficar sem memória. Ficar sem memória faz com que a sessão atual seja encerrada (falha) e todas as sessões simultâneas nessa instância do servidor recebem uma exceção de falta de memória. Para evitar que esse cenário ocorra, o aplicativo deve usar uma estrutura de dados que imponha um limite de itens aos usuários simultâneos.
  • Se um esquema de paginação não for usado para renderização, o servidor usará memória adicional para os objetos que não estão visíveis na interface do usuário. Sem um limite no número de itens, as demandas de memória podem esgotar a memória do servidor disponível. Para evitar esse cenário, use uma das seguintes abordagens:
    • Use listas paginadas ao renderizar.
    • Exiba apenas os primeiros 100 a 1.000 itens e exija que o usuário insira critérios de pesquisa para localizar itens, além dos itens exibidos.
    • Para um cenário de renderização mais avançado, implemente listas ou grades compatíveis com a virtualização. Usando a virtualização, as listas renderizam apenas um subconjunto de itens visíveis no momento para o usuário. Quando o usuário interage com a barra de rolagem na interface do usuário, o componente renderiza apenas os itens necessários para exibição. Os itens que não são necessários no momento para exibição podem ser mantidos no armazenamento secundário, o que é a abordagem ideal. Os itens não reproduzidos também podem ser mantidos na memória, o que é menos ideal.

Observação

Blazor tem suporte interno para virtualização. Para obter mais informações, confira Virtualização de componentes Razor do ASP.NET Core.

Os aplicativos Blazor oferecem um modelo de programação semelhante a outras estruturas de interface do usuário para aplicativos com estado, como WPF, Windows Forms ou Blazor WebAssembly. A principal diferença é que, em várias das estruturas de interface do usuário, a memória consumida pelo aplicativo pertence ao cliente e afeta apenas esse cliente individual. Por exemplo, um aplicativo Blazor WebAssembly é executado inteiramente no cliente e usa apenas recursos de memória do cliente. Em um aplicativo Blazor do servidor, a memória consumida pelo aplicativo pertence ao servidor e é compartilhada entre os clientes na instância do servidor.

As demandas de memória do servidor são uma consideração para todos os aplicativos Blazor do servidor. No entanto, a maioria dos aplicativos Web são sem estado e a memória usada durante o processamento de uma solicitação é liberada quando a resposta é retornada. Como recomendação geral, não permita que os clientes aloquem um volume de memória desassociado como em qualquer outro aplicativo do lado do servidor que persista as conexões do cliente. A memória consumida por um aplicativo Blazor do servidor persiste por mais tempo do que uma solicitação individual.

Observação

Durante o desenvolvimento, um criador de perfil pode ser usado ou um rastreamento capturado para avaliar as demandas de memória dos clientes. Um criador de perfil ou rastreamento não capturará a memória alocada a um cliente específico. Para capturar o uso de memória de um cliente específico durante o desenvolvimento, capture um despejo e examine a demanda de memória de todos os objetos com raiz no circuito de um usuário.

Conexões de cliente

O esgotamento da conexão pode ocorrer quando um ou mais clientes abrem muitas conexões simultâneas com o servidor, impedindo que outros clientes estabeleçam novas conexões.

Os clientes Blazor estabelecem uma única conexão por sessão e mantêm a conexão aberta enquanto a janela do navegador estiver aberta. Dada a natureza persistente das conexões e a natureza com estado dos aplicativos Blazor do servidor, o esgotamento da conexão é um risco maior para a disponibilidade do aplicativo.

Não há limite no número de conexões por usuário para um aplicativo. Se o aplicativo exigir um limite de conexão, use uma ou mais das seguintes abordagens:

  • Exija autenticação, o que naturalmente limita a capacidade de usuários não autorizados se conectarem ao aplicativo. Para que esse cenário seja eficaz, os usuários devem ser impedidos de provisionar novos usuários sob demanda.
  • Limite o número de conexões por usuário. As conexões podem ser limitadas por meio das abordagens a seguir. Tenha cuidado para permitir que usuários legítimos acessem o aplicativo (por exemplo, quando um limite de conexão é estabelecido com base no endereço IP do cliente).
    • Nível de aplicativo
      • Extensibilidade de roteamento de ponto de extremidade.
      • Exija autenticação para se conectar ao aplicativo e acompanhar as sessões ativas por usuário.
      • Rejeite novas sessões ao atingir um limite.
      • Conexões WebSocket de proxy com um aplicativo por meio do uso de um proxy, como o Serviço do Azure SignalR que multiplexa as conexões de clientes para um aplicativo. Isso fornece um aplicativo com maior capacidade de conexão do que um único cliente pode estabelecer, impedindo que um cliente esgote as conexões com o servidor.
    • Nível de servidor
  • Exija autenticação, o que naturalmente limita a capacidade de usuários não autorizados se conectarem ao aplicativo. Para que esse cenário seja eficaz, os usuários devem ser impedidos de provisionar novos usuários sob demanda.
  • Limite o número de conexões por usuário. As conexões podem ser limitadas por meio das abordagens a seguir. Tenha cuidado para permitir que usuários legítimos acessem o aplicativo (por exemplo, quando um limite de conexão é estabelecido com base no endereço IP do cliente).
    • Nível de aplicativo
      • Extensibilidade de roteamento de ponto de extremidade.
      • Exija autenticação para se conectar ao aplicativo e acompanhar as sessões ativas por usuário.
      • Rejeite novas sessões ao atingir um limite.
      • Conexões WebSocket de proxy com um aplicativo por meio do uso de um proxy, como o Serviço do Azure SignalR que multiplexa as conexões de clientes para um aplicativo. Isso fornece um aplicativo com maior capacidade de conexão do que um único cliente pode estabelecer, impedindo que um cliente esgote as conexões com o servidor.
    • Nível de servidor

Ataques de DoS (negação de serviço)

Ataques de negação de serviço (DoS) envolvem um cliente fazendo com que o servidor esgote um ou mais de seus recursos tornando o aplicativo indisponível. Os aplicativos Blazor incluem limites padrão e dependem de outros limites do ASP.NET Core e SignalR que são definidos em CircuitOptions para proteger contra ataques de DoS:

Para obter mais informações e exemplos de codificação de configuração, confira os seguintes artigos:

Interações com o navegador (cliente)

Um cliente interage com o servidor por meio da expedição de eventos de interoperabilidade e da conclusão da renderização JS. A comunicação de interoperabilidade JS vai para os dois lados entre o JavaScript e o .NET:

  • Os eventos do navegador são expedidos do cliente para o servidor de forma assíncrona.
  • O servidor responde de forma assíncrona renderizando novamente a interface do usuário conforme necessário.

Funções do JavaScript invocadas a partir do .NET

Para chamadas que usam os métodos do .NET para o JavaScript:

  • Todas as invocações têm um tempo limite configurável após o qual elas falham, retornando um OperationCanceledException para o chamador.
  • O resultado de uma chamada do JavaScript não pode ser confiável. O cliente Blazor do aplicativo em execução no navegador procura a função do JavaScript a ser invocada. A função é invocada e o resultado ou um erro é produzido. Um cliente mal-intencionado pode tentar:
    • Causar um problema no aplicativo retornando um erro da função do JavaScript.
    • Induzir um comportamento não intencional no servidor, retornando um resultado inesperado da função da JavaScript.

Tome as seguintes precauções para se proteger contra os cenários anteriores:

  • Encapsule as chamadas de interoperabilidade JS em instruções try-catch para considerar erros que podem ocorrer durante as invocações. Para obter mais informações, confira Manipular erros nos aplicativos Blazor do ASP.NET Core.
  • Valide os dados retornados das invocações de interoperabilidade JS, incluindo mensagens de erro, antes de executar qualquer ação.

Métodos do .NET invocados no navegador

Não confie em chamadas que usam métodos do JavaScript para o .NET. Quando um método do .NET é exposto ao JavaScript, considere como o método do .NET é invocado:

  • Trate qualquer método do .NET exposto ao JavaScript como você faria com um ponto de extremidade público para o aplicativo.
    • Valide a entrada.
      • Verifique se os valores estão dentro dos intervalos esperados.
      • Verifique se o usuário tem permissão para executar a ação solicitada.
    • Não aloque recursos excessivos como parte da invocação do método do .NET. Por exemplo, execute verificações e coloque limites no uso de CPU e memória.
    • Leve em conta que os métodos estáticos e de instância podem ser expostos a clientes JavaScript. Evite compartilhar o estado entre as sessões, a menos que o design exija o compartilhamento do estado com as restrições adequadas.
      • Por exemplo, os métodos expostos por meio de objetos DotNetObjectReference criados originalmente por meio de DI (injeção de dependência), os objetos devem ser registrados como objetos com escopo. Isso se aplica a qualquer serviço de DI usado pelo aplicativo .
      • Para métodos estáticos, evite estabelecer um estado que não possa ser definido como escopo para o cliente, a menos que o aplicativo esteja compartilhando explicitamente o estado por design entre todos os usuários em uma instância de servidor.
    • Evite passar dados fornecidos pelo usuário em parâmetros para as chamadas do JavaScript. Se passar dados em parâmetros for absolutamente necessário, verifique se o código JavaScript faz a passagem dos dados sem introduzir vulnerabilidades de XSS (cross-site scripting). Por exemplo, não grave os dados fornecidos pelo usuário no DOM definido a propriedade innerHTML de um elemento. Use a CSP (Política de Segurança de Conteúdo) para desabilitar eval e outros primitivos JavaScript desprotegidos. Para obter mais informações, confira Impor uma política de segurança de conteúdo para Blazor ASP.NET Core.
  • Evite implementar a expedição personalizada de invocações do .NET sobre a implementação de expedição da estrutura. Expor os métodos do .NET ao navegador é um cenário avançado, não recomendado para desenvolvimento geral Blazor.

Eventos

Os eventos fornecem um ponto de entrada para um aplicativo. As mesmas regras para proteger pontos de extremidade em aplicativos Web se aplicam à manipulação de eventos em aplicativos Blazor. Um cliente mal-intencionado pode enviar todos os dados que deseja enviar como o conteúdo de um evento.

Por exemplo:

  • Um evento de alteração para um <select> pode enviar um valor que não está dentro das opções que o aplicativo apresentou ao cliente.
  • Um <input> pode enviar quaisquer dados de texto para o servidor, ignorando a validação do lado do cliente.

O aplicativo deve validar os dados para qualquer evento realizado pelo aplicativo. Os Blazorcomponentes de formulários da estrutura executam as validações básicas. Se o aplicativo usar componentes de formulários personalizados, o código personalizado deverá ser gravado para validar os dados do evento, conforme apropriado.

Os eventos são assíncronos, ou seja, vários eventos podem ser enviados ao servidor antes que o aplicativo tenha tempo para reagir produzindo uma nova renderização. Isso tem algumas implicações de segurança a serem consideradas. As ações do cliente no aplicativo devem ser limitadas dentro dos manipuladores de eventos e não devem depender do estado de exibição renderizado atual.

Considere um componente de contador que deve permitir que um usuário incremente um contador três vezes, no máximo. O botão para incrementar o contador é condicionalmente baseado no valor de count:

<p>Count: @count</p>

@if (count < 3)
{
    <button @onclick="IncrementCount" value="Increment count" />
}

@code 
{
    private int count = 0;

    private void IncrementCount()
    {
        count++;
    }
}

Um cliente pode expedir um ou mais eventos de incremento, antes que a estrutura produza uma nova renderização desse componente. O resultado é que o count pode ser incrementado mais de três vezes pelo usuário, pois o botão não é removido pela interface do usuário rapidamente o suficiente. A maneira correta de atingir o limite de três incrementos count é mostrada no exemplo a seguir:

<p>Count: @count</p>

@if (count < 3)
{
    <button @onclick="IncrementCount" value="Increment count" />
}

@code 
{
    private int count = 0;

    private void IncrementCount()
    {
        if (count < 3)
        {
            count++;
        }
    }
}

Ao adicionar a marca if (count < 3) { ... } dentro do manipulador, a decisão de incrementar count é baseada no estado atual do aplicativo. A decisão não se baseia no estado da interface do usuário como era no exemplo anterior, que pode estar temporariamente obsoleto.

Proteção contra várias expedições

Se um retorno de chamada de evento invocar uma operação de execução prolongada de forma assíncrona, como buscar dados de um serviço ou banco de dados externo, use uma proteção. A proteção pode impedir o usuário de enfileirar várias operações enquanto a operação estiver em andamento com feedback visual. O código do componente a seguir define isLoading como true, enquanto DataService.GetDataAsync obtém dados do servidor. Enquanto isLoading for true, o botão estará desabilitado na interface do usuário:

<button disabled="@isLoading" @onclick="UpdateData">Update</button>

@code {
    private bool isLoading;
    private Data[] data = Array.Empty<Data>();

    private async Task UpdateData()
    {
        if (!isLoading)
        {
            isLoading = true;
            data = await DataService.GetDataAsync(DateTime.Now);
            isLoading = false;
        }
    }
}

O padrão de proteção demonstrado no exemplo anterior funcionará se a operação em segundo plano for executada de forma assíncrona com o padrão async-await.

Cancelar antecipadamente e evite o uso após o descarte

Além de usar uma proteção, conforme descrito na seção Proteção contra várias expedições, use um CancellationToken para cancelar as operações de execução longa quando o componente for descartado. Essa abordagem tem o benefício adicional de evitar o uso após o descarte em componentes:

@implements IDisposable

...

@code {
    private readonly CancellationTokenSource TokenSource = 
        new CancellationTokenSource();

    private async Task UpdateData()
    {
        ...

        data = await DataService.GetDataAsync(DateTime.Now, TokenSource.Token);

        if (TokenSource.Token.IsCancellationRequested)
        {
           return;
        }

        ...
    }

    public void Dispose()
    {
        TokenSource.Cancel();
    }
}

Evite os eventos que produzem grandes volumes de dados

Alguns eventos de DOM, como oninput ou onscroll, podem produzir um grande volume de dados. Evite usar esses eventos em aplicativos Blazor do servidor.

Diretrizes de segurança adicionais

As diretrizes usadas para proteger aplicativos ASP.NET Core se destinam aos aplicativos Blazor do servidor e são abordadas nas seguintes seções deste artigo:

Log e dados confidenciais

As interações de interoperabilidade JS entre o cliente e o servidor são registradas nos logs do servidor com instâncias ILogger. Blazor evita o log de informações confidenciais, como eventos reais ou entradas e saídas de interoperabilidade JS.

Quando ocorre um erro no servidor, a estrutura notifica o cliente e rasga a sessão. Por padrão, o cliente recebe uma mensagem de erro genérica que pode ser vista nas ferramentas de desenvolvedor do navegador.

O erro do lado do cliente não inclui a pilha de chamadas e não fornece detalhes sobre a causa do erro, mas os logs do servidor contêm essas informações. Para fins de desenvolvimento, as informações confidenciais de erro podem ser disponibilizadas para o cliente habilitando erros detalhados.

Aviso

Expor informações de erro a clientes na Internet é um risco de segurança que sempre deve ser evitado.

Proteger informações em trânsito com HTTPS

Blazor usa SignalR para comunicação entre o cliente e o servidor. Blazor normalmente usa o transporte que SignalR negocia, que normalmente é WebSockets.

Blazor não garante a integridade e a confidencialidade dos dados enviados entre o servidor e o cliente. Sempre usa HTTPS.

XSS (cross-site scripting)

O XSS (cross-site scripting) permite que uma parte não autorizada execute uma lógica arbitrária no contexto do navegador. Um aplicativo comprometido pode executar um código arbitrário no cliente. A vulnerabilidade pode ser usada para possivelmente executar várias ações mal-intencionadas no servidor:

  • Expeça eventos falsos/inválidos para o servidor.
  • Expeça de conclusões de renderização com falha/inválidas.
  • Evite expedir conclusões de renderização.
  • Expeça chamadas de interoperabilidade do JavaScript para o .NET.
  • Modifique a resposta de chamadas de interoperabilidade do .NET para o JavaScript.
  • Evite expedir os resultados de interoperabilidade do .NET para o JS.

A estrutura Blazor executa etapas para proteger contra algumas das ameaças anteriores:

  • Interromperá a produção de novas atualizações de interface do usuário, se o cliente não estiver reconhecendo os lotes de renderização. Configurado com CircuitOptions.MaxBufferedUnacknowledgedRenderBatches.
  • Atinge o tempo limite de qualquer chamada do .NET para o JavaScript, após um minuto sem receber uma resposta do cliente. Configurado com CircuitOptions.JSInteropDefaultCallTimeout.
  • Executa a validação básica em todas as entradas provenientes do navegador durante a interoperabilidade JS:
    • As referências do .NET são válidas e do tipo esperado pelo método do .NET.
    • Os dados não estão malformados.
    • O número correto de argumentos para o método está presente na carga.
    • Os argumentos ou resultados podem ser desserializados corretamente, antes de invocar o método.
  • Executa a validação básica em todas as entradas provenientes do navegador de eventos expedidos:
    • O evento tem um tipo válido.
    • Os dados do evento podem ser desserializados.
    • Há um manipulador de eventos associado ao evento.

Além das proteções implementadas pela estrutura, o aplicativo deve ser codificado pelo desenvolvedor para proteger contra ameaças e tomar as medidas apropriadas:

  • Sempre valide os dados ao lidar com eventos.
  • Tome as medidas apropriadas ao receber dados inválidos:
    • Ignore os dados e retorne. Isso permite que o aplicativo continue processando as solicitações.
    • Se o aplicativo determinar que a entrada não é legítima e não poderia ser produzida por um cliente legítimo, gere uma exceção. Lançar uma exceção derruba o circuito e encerra a sessão.
  • Não confie na mensagem de erro fornecida pelas conclusões em lote de renderização incluídas nos logs. O erro é fornecido pelo cliente e geralmente não pode ser confiável, pois o cliente pode estar comprometido.
  • Não confie na entrada em chamadas de interoperabilidade JS em qualquer direção entre os métodos do JavaScript e do .NET.
  • O aplicativo é responsável por validar se o conteúdo de argumentos e resultados é válido, mesmo que os argumentos ou resultados sejam desserializados corretamente.

Para que uma vulnerabilidade de XSS exista, o aplicativo deve incorporar a entrada do usuário na página renderizada. O Blazor executa uma etapa de tempo de compilação em que a marcação em um arquivo .razor é transformada em uma lógica de procedimentos do C#. No runtime, a lógica do C# cria uma árvore de renderização que descreve os elementos, o texto e os componentes filho. Isso é aplicado ao DOM do navegador por meio de uma sequência de instruções do JavaScript (ou é serializado para HTML no caso de pré-renderização):

  • A entrada do usuário renderizada por meio da sintaxe normal Razor (por exemplo, @someStringValue) não expõe uma vulnerabilidade de XSS, pois a sintaxe Razor é adicionada ao DOM por meio de comandos que só podem gravar texto. Mesmo que o valor inclua a marcação HTML, o valor será exibido como texto estático. Durante a pré-renderização, a saída é codificada em HTML, que também exibe o conteúdo como texto estático.
  • As marcas de script não são permitidas e não devem ser incluídas na árvore de renderização de componentes do aplicativo. Se uma marca de script estiver incluída na marcação de um componente, um erro de tempo de compilação será gerado.
  • Os autores de componentes podem criar componentes no C#, sem usar Razor. O autor do componente é responsável por usar as APIs corretas ao emitir a saída. Por exemplo, use builder.AddContent(0, someUserSuppliedString) e não builder.AddMarkupContent(0, someUserSuppliedString), pois o último poderia criar uma vulnerabilidade XSS.

Mitigue ainda mais as vulnerabilidades de XSS. Por exemplo, implemente uma CSP (Política de Segurança de Conteúdo) restritiva. Para obter mais informações, confira Impor uma política de segurança de conteúdo para Blazor ASP.NET Core.

Para obter mais informações, confira Impedir XSS (cross-site scripting) no ASP.NET Core.

Proteção entre origens

Os ataques entre origens envolvem um cliente de uma origem diferente executando uma ação contra o servidor. A ação mal-intencionada normalmente é uma solicitação GET ou um formulário POST (CSRF, Solicitação Intersite Forjada), mas abrir um WebSocket mal-intencionado também é possível. Os aplicativos Blazor oferecem as mesmas garantias de que qualquer outro aplicativo SignalR que use a oferta de protocolo hub:

  • Os aplicativos podem ser acessados entre origens, a menos que medidas adicionais sejam tomadas para impedi-lo. Para desabilitar o acesso entre origens, desabilite o CORS no ponto de extremidade adicionando o Middleware CORS ao pipeline e adicionando o DisableCorsAttribute aos metadados de ponto de extremidade Blazor ou limite o conjunto de origens permitidas por , configurando SignalR para o Compartilhamento de Recursos entre Origens. Para obter diretrizes sobre restrições de origem do WebSocket, confira Suporte a WebSockets no ASP.NET Core.
  • Se o CORS estiver habilitado, etapas adicionais podem ser necessárias para proteger o aplicativo, dependendo da configuração do CORS. Se o CORS estiver habilitado globalmente, o CORS pode ser desabilitado para o hub BlazorSignalR adicionando os metadados DisableCorsAttribute aos metadados de ponto de extremidade, depois de chamar MapBlazorHub no construtor de rotas do ponto de extremidade.

Para obter mais informações, consulte Impedir ataques de XSRF/CSRF (solicitação intersite forjada) no ASP.NET Core.

Clickjacking

O clickjacking envolve renderizar um site como um <iframe> dentro de um site de uma origem diferente para enganar o usuário para executar ações no site sob ataque.

Para proteger um aplicativo contra renderização dentro de um <iframe>, use a CSP (Política de Segurança de Conteúdo) e o cabeçalho X-Frame-Options.

Para saber mais, consulte os recursos a seguir:

Redirecionamentos em aberto

Quando uma sessão do aplicativo é iniciada, o servidor executa a validação básica das URLs enviadas como parte do início da sessão. A estrutura verifica se a URL base é um pai da URL atual, antes de estabelecer o circuito. Nenhuma verificação adicional é executada pela estrutura.

Quando um usuário seleciona um link no cliente, a URL do link é enviada ao servidor, que determina qual ação deve ser tomada. Por exemplo, o aplicativo pode executar uma navegação do lado do cliente ou indicar ao navegador para ir para o novo local.

Os componentes também podem disparar solicitações de navegação programaticamente por meio do uso de NavigationManager. Nesses cenários, o aplicativo pode executar uma navegação do lado do cliente ou indicar ao navegador para ir para o novo local.

Os componentes devem:

  • Evitar o uso de entrada do usuário como parte dos argumentos de chamada de navegação.
  • Validar os argumentos para garantir que o destino seja permitido pelo aplicativo.

Caso contrário, um usuário mal-intencionado pode forçar o navegador a ir para um site controlado pelo invasor cibernético. Nesse cenário, o invasor cibernético engana o aplicativo para usar alguma entrada do usuário como parte da invocação do método NavigationManager.NavigateTo.

Esse conselho também se aplica ao renderizar links como parte do aplicativo:

  • Se possível, use links relativos.
  • Valide se os destinos de link absoluto são válidos, antes de incluí-los em uma página.

Para obter mais informações, confira Impedir ataques de redirecionamento em aberto no ASP.NET Core.

Lista de verificação de segurança

A seguinte lista de considerações de segurança não está completa:

  • Valide os argumentos de eventos.
  • Valide entradas e resultados de chamadas de interoperabilidade JS.
  • Evite usar (ou validar antecipadamente) a entrada do usuário para .NET para a interoperabilidade de chamadas JS.
  • Impeça que o cliente aloque uma quantidade de memória não associada.
  • Proteja-se contra várias expedições.
  • Cancele as operações de execução prolongada, quando o componente for descartado.
  • Evite os eventos que produzem grandes volumes de dados.
  • Evite usar a entrada do usuário como parte de chamadas para NavigationManager.NavigateTo e valide a entrada do usuário para URLs em relação a um conjunto de origens permitidas primeiro, se inevitável.
  • Não tome decisões de autorização com base no estado da interface do usuário, mas apenas no estado do componente.
  • Use a CSP (Política de Segurança de Conteúdo) para proteger-se contra ataques de XSS. Para obter mais informações, confira Impor uma política de segurança de conteúdo para Blazor ASP.NET Core.
  • Use CSP e X-Frame-Options para proteger-se contra clickjacking.
  • Verifique se as configurações de CORS são apropriadas ao habilitar o CORS ou desabilitar explicitamente o CORS para aplicativos Blazor.
  • Teste para garantir que os limites do lado do servidor para o aplicativo Blazor forneçam uma experiência aceitável do usuário, sem níveis de risco inaceitáveis.