Partilhar via


Introdução à segurança do SignalR

por Patrick Fletcher, Tom FitzMacken

Aviso

Esta documentação não é para a versão mais recente do SignalR. Dê uma olhada em ASP.NET Core SignalR.

Este artigo descreve os problemas de segurança que você deve considerar ao desenvolver um aplicativo SignalR.

Versões de software usadas neste tópico

Versões anteriores deste tópico

Para obter informações sobre versões anteriores do SignalR, consulte Versões mais antigas do SignalR.

Perguntas e comentários

Deixe comentários sobre como você gostou deste tutorial e o que poderíamos melhorar nos comentários na parte inferior da página. Se você tiver dúvidas que não estão diretamente relacionadas ao tutorial, poderá postá-las no fórum do ASP.NET SignalR ou StackOverflow.com.

Visão geral

Este documento contém as seguintes seções:

Conceitos de segurança do SignalR

Autenticação e autorização

O SignalR não fornece nenhum recurso para autenticar usuários. Em vez disso, você integra os recursos do SignalR à estrutura de autenticação existente de um aplicativo. Você autentica os usuários como faria normalmente em seu aplicativo e trabalha com os resultados da autenticação no código do SignalR. Por exemplo, você pode autenticar seus usuários com ASP.NET autenticação de formulários e, em seguida, no hub, impor quais usuários ou funções estão autorizados a chamar um método. No hub, você também pode passar informações de autenticação, como nome de usuário ou se um usuário pertence a uma função, para o cliente.

O SignalR fornece o atributo Authorize para especificar quais usuários têm acesso a um hub ou método. Você aplica o atributo Authorize a um hub ou a métodos específicos em um hub. Sem o atributo Authorize, todos os métodos públicos no hub estão disponíveis para um cliente conectado ao hub. Para obter mais informações sobre hubs, consulte Autenticação e autorização para Hubs do SignalR.

Você aplica o Authorize atributo a hubs, mas não a conexões persistentes. Para impor regras de autorização ao usar um PersistentConnection , você deve substituir o AuthorizeRequest método . Para obter mais informações sobre conexões persistentes, consulte Autenticação e autorização para conexões persistentes do SignalR.

Token de conexão

O SignalR reduz o risco de executar comandos mal-intencionados validando a identidade do remetente. Para cada solicitação, o cliente e o servidor passam um token de conexão que contém a ID de conexão e o nome de usuário para usuários autenticados. A ID de conexão identifica exclusivamente cada cliente conectado. O servidor gera aleatoriamente a ID de conexão quando uma nova conexão é criada e persiste essa ID durante a conexão. O mecanismo de autenticação para o aplicativo Web fornece o nome de usuário. O SignalR usa criptografia e uma assinatura digital para proteger o token de conexão.

Diagrama que mostra uma seta da Solicitação de Nova Conexão do Cliente para a Solicitação de Conexão Recebida do Servidor para Resposta do Servidor à Resposta Recebida do Cliente. O Sistema de Autenticação gera um Token de Conexão nas caixas Resposta e Resposta Recebida.

Para cada solicitação, o servidor valida o conteúdo do token para garantir que a solicitação venha do usuário especificado. O nome de usuário deve corresponder à ID da conexão. Ao validar a ID de conexão e o nome de usuário, o SignalR impede que um usuário mal-intencionado represente facilmente outro usuário. Se o servidor não puder validar o token de conexão, a solicitação falhará.

Diagrama que mostra uma seta da Solicitação do Cliente para a Solicitação Recebida do Servidor para o Token Salvo. O Token de Conexão e a Mensagem estão na caixa Cliente e na caixa Servidor.

Como a ID de conexão faz parte do processo de verificação, você não deve revelar a ID de conexão de um usuário para outros usuários nem armazenar o valor no cliente, como em um cookie.

Tokens de conexão versus outros tipos de token

Os tokens de conexão ocasionalmente são sinalizados por ferramentas de segurança porque parecem ser tokens de sessão ou tokens de autenticação, o que representa um risco se expostos.

O token de conexão do SignalR não é um token de autenticação. Ele é usado para confirmar se o usuário que está fazendo essa solicitação é o mesmo que criou a conexão. O token de conexão é necessário porque ASP.NET SignalR permite que as conexões se movam entre servidores. O token associa a conexão a um usuário específico, mas não declara a identidade do usuário que está fazendo a solicitação. Para que uma solicitação do SignalR seja autenticada corretamente, ela deve ter algum outro token que declare a identidade do usuário, como um cookie ou token de portador. No entanto, o token de conexão em si não faz nenhuma declaração de que a solicitação foi feita por esse usuário, apenas que a ID de conexão contida no token está associada a esse usuário.

Como o token de conexão não fornece nenhuma declaração de autenticação própria, ele não é considerado um token de "sessão" ou "autenticação". Pegar o token de conexão de um determinado usuário e reproduzi-lo em uma solicitação autenticada como um usuário diferente (ou uma solicitação não autenticada) falhará, pois a identidade do usuário da solicitação e a identidade armazenada no token não corresponderão.

Reingressar em grupos ao se reconectar

Por padrão, o aplicativo SignalR reatribuirá automaticamente um usuário aos grupos apropriados ao se reconectar de uma interrupção temporária, como quando uma conexão é descartada e restabelecida antes do tempo limite da conexão. Ao se reconectar, o cliente passa um token de grupo que inclui a ID de conexão e os grupos atribuídos. O token de grupo é assinado digitalmente e criptografado. O cliente retém a mesma ID de conexão após uma reconexão; Portanto, a ID de conexão passada do cliente reconectado deve corresponder à ID de conexão anterior usada pelo cliente. Essa verificação impede que um usuário mal-intencionado passe solicitações para ingressar em grupos não autorizados ao se reconectar.

No entanto, é importante observar que o token de grupo não expira. Se um usuário pertencia a um grupo no passado, mas foi banido desse grupo, esse usuário pode ser capaz de imitar um token de grupo que inclui o grupo proibido. Se você precisar gerenciar com segurança quais usuários pertencem a quais grupos, precisará armazenar esses dados no servidor, como em um banco de dados. Em seguida, adicione lógica ao aplicativo que verifica no servidor se um usuário pertence a um grupo. Para obter um exemplo de verificação de associação de grupo, consulte Trabalhando com grupos.

A reingressão automática de grupos só se aplica quando uma conexão é reconectada após uma interrupção temporária. Se um usuário se desconectar navegando para longe do aplicativo ou o aplicativo for reiniciado, seu aplicativo deverá lidar com como adicionar esse usuário aos grupos corretos. Para obter mais informações, consulte Trabalhando com grupos.

Como o SignalR impede a solicitação intersite forjada

CSRF (Solicitação Intersite Forjada) é um ataque em que um site mal-intencionado envia uma solicitação para um site vulnerável em que o usuário está conectado no momento. O SignalR impede o CSRF, tornando extremamente improvável que um site mal-intencionado crie uma solicitação válida para seu aplicativo SignalR.

Descrição do ataque CSRF

Aqui está um exemplo de um ataque CSRF:

  1. Um usuário faz logon em www.example.com, usando a autenticação de formulários.

  2. O servidor autentica o usuário. A resposta do servidor inclui um cookie de autenticação.

  3. Sem fazer logoff, o usuário visita um site mal-intencionado. Este site mal-intencionado contém o seguinte formulário HTML:

    <h1>You Are a Winner!</h1>
    <form action="http://example.com/api/account" method="post">
        <input type="hidden" name="Transaction" value="withdraw" />
        <input type="hidden" name="Amount" value="1000000" />
        <input type="submit" value="Click Me"/>
    </form>
    

    Observe que a ação de formulário é posta no site vulnerável, não no site mal-intencionado. Esta é a parte "entre sites" do CSRF.

  4. O usuário clica no botão Enviar. O navegador inclui o cookie de autenticação com a solicitação.

  5. A solicitação é executada no servidor example.com com o contexto de autenticação do usuário e pode fazer qualquer coisa que um usuário autenticado tenha permissão para fazer.

Embora este exemplo exija que o usuário clique no botão de formulário, a página mal-intencionada pode executar facilmente um script que envia uma solicitação AJAX para seu aplicativo SignalR. Além disso, o uso de SSL não impede um ataque CSRF, pois o site mal-intencionado pode enviar uma solicitação de "https://".

Normalmente, ataques CSRF são possíveis em sites que usam cookies para autenticação, pois os navegadores enviam todos os cookies relevantes para o site de destino. No entanto, os ataques CSRF não se limitam à exploração de cookies. Por exemplo, a autenticação Básica e Digest também são vulneráveis. Depois que um usuário faz logon com a autenticação Básica ou Digest, o navegador envia automaticamente as credenciais até que a sessão termine.

Mitigações de CSRF tomadas pelo SignalR

O SignalR executa as etapas a seguir para impedir que um site mal-intencionado crie solicitações válidas para seu aplicativo. O SignalR executa essas etapas por padrão, você não precisa executar nenhuma ação em seu código.

  • Desabilitar solicitações entre domínios O SignalR desabilita solicitações entre domínios para impedir que os usuários chamem um ponto de extremidade do SignalR de um domínio externo. O SignalR considera qualquer solicitação de um domínio externo inválida e bloqueia a solicitação. Recomendamos que você mantenha esse comportamento padrão; caso contrário, um site mal-intencionado poderia enganar os usuários para enviar comandos ao seu site. Se você precisar usar solicitações entre domínios, consulte Como estabelecer uma conexão entre domínios .
  • Passar token de conexão na cadeia de caracteres de consulta, não no cookie O SignalR passa o token de conexão como um valor de cadeia de caracteres de consulta, em vez de como um cookie. Armazenar o token de conexão em um cookie não é seguro porque o navegador pode encaminhar inadvertidamente o token de conexão quando um código mal-intencionado é encontrado. Além disso, passar o token de conexão na cadeia de caracteres de consulta impede que o token de conexão persista além da conexão atual. Portanto, um usuário mal-intencionado não pode fazer uma solicitação sob as credenciais de autenticação de outro usuário.
  • Verificar token de conexão Conforme descrito na seção Token de conexão , o servidor sabe qual ID de conexão está associada a cada usuário autenticado. O servidor não processa nenhuma solicitação de uma ID de conexão que não corresponda ao nome de usuário. É improvável que um usuário mal-intencionado possa adivinhar uma solicitação válida porque o usuário mal-intencionado teria que saber o nome de usuário e a ID de conexão gerada aleatoriamente no momento. Essa ID de conexão se torna inválida assim que a conexão é encerrada. Os usuários anônimos não devem ter acesso a nenhuma informação confidencial.

Recomendações de segurança do SignalR

Protocolo SSL

O protocolo SSL usa criptografia para proteger o transporte de dados entre um cliente e um servidor. Se o aplicativo SignalR transmitir informações confidenciais entre o cliente e o servidor, use SSL para o transporte. Para obter mais informações sobre como configurar o SSL, consulte Como configurar o SSL no IIS 7.

Não usar grupos como um mecanismo de segurança

Os grupos são uma maneira conveniente de coletar usuários relacionados, mas não são um mecanismo seguro para limitar o acesso a informações confidenciais. Isso é especialmente verdadeiro quando os usuários podem reingressar automaticamente em grupos durante uma reconexão. Em vez disso, considere adicionar usuários privilegiados a uma função e limitar o acesso a um método de hub a apenas membros dessa função. Para obter um exemplo de restrição de acesso com base em uma função, consulte Autenticação e autorização para Hubs do SignalR. Para obter um exemplo de verificação do acesso do usuário a grupos ao se reconectar, consulte Trabalhando com grupos.

Manipulando com segurança a entrada de clientes

Para garantir que um usuário mal-intencionado não envie script para outros usuários, você deve codificar todas as entradas de clientes destinados à difusão para outros clientes. Você deve codificar mensagens nos clientes receptores em vez do servidor, pois seu aplicativo SignalR pode ter muitos tipos diferentes de clientes. Portanto, a codificação HTML funciona para um cliente Web, mas não para outros tipos de clientes. Por exemplo, um método de cliente Web para exibir uma mensagem de chat trataria com segurança o nome de usuário e a mensagem chamando a html() função .

chat.client.addMessageToPage = function (name, message) {
    // Html encode display name and message. 
    var encodedName = $('<div />').text(name).html();
    var encodedMsg = $('<div />').text(message).html();
    // Add the message to the page. 
    $('#discussion').append('<li><strong>' + encodedName
        + '</strong>:  ' + encodedMsg + '</li>');
};

Reconciliando uma alteração no status do usuário com uma conexão ativa

Se a autenticação de um usuário status for alterada enquanto houver uma conexão ativa, o usuário receberá um erro informando: "A identidade do usuário não pode ser alterada durante uma conexão do SignalR ativa". Nesse caso, seu aplicativo deve se conectar novamente ao servidor para garantir que a ID de conexão e o nome de usuário sejam coordenados. Por exemplo, se o aplicativo permitir que o usuário faça logoff enquanto houver uma conexão ativa, o nome de usuário da conexão não corresponderá mais ao nome passado para a próxima solicitação. Você desejará interromper a conexão antes que o usuário faça logoff e reiniciá-la.

No entanto, é importante observar que a maioria dos aplicativos não precisará parar e iniciar a conexão manualmente. Se o aplicativo redirecionar os usuários para uma página separada após o logoff, como o comportamento padrão em um aplicativo Web Forms ou aplicativo MVC, ou atualizar a página atual após o logoff, a conexão ativa será desconectada automaticamente e não exigirá nenhuma ação adicional.

O exemplo a seguir mostra como parar e iniciar uma conexão quando o usuário status foi alterado.

<script type="text/javascript">
    $(function () {
        var chat = $.connection.sampleHub;
        $.connection.hub.start().done(function () {
            $('#logoutbutton').click(function () {
                chat.connection.stop();
                $.ajax({
                    url: "Services/SampleWebService.svc/LogOut",
                    type: "POST"
                }).done(function () {
                    chat.connection.start();
                });
            });
        });
    });
</script>

Ou, o status de autenticação do usuário poderá mudar se o site usar a expiração deslizante com a Autenticação de Formulários e não houver nenhuma atividade para manter o cookie de autenticação válido. Nesse caso, o usuário será desconectado e o nome de usuário não corresponderá mais ao nome de usuário no token de conexão. Você pode corrigir esse problema adicionando algum script que solicita periodicamente um recurso no servidor Web para manter o cookie de autenticação válido. O exemplo a seguir mostra como solicitar um recurso a cada 30 minutos.

$(function () {
    setInterval(function() {
        $.ajax({
            url: "Ping.aspx",
            cache: false
        });
    }, 1800000);
});

Arquivos de proxy JavaScript gerados automaticamente

Se você não quiser incluir todos os hubs e métodos no arquivo proxy JavaScript para cada usuário, poderá desabilitar a geração automática do arquivo. Você poderá escolher essa opção se tiver vários hubs e métodos, mas não quiser que todos os usuários estejam cientes de todos os métodos. Desabilite a geração automática definindo EnableJavaScriptProxies comofalse.

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableJavaScriptProxies = false;
app.MapSignalR(hubConfiguration);

Para obter mais informações sobre os arquivos de proxy JavaScript, consulte O proxy gerado e o que ele faz por você.

Exceções

Você deve evitar passar objetos de exceção para clientes porque os objetos podem expor informações confidenciais aos clientes. Em vez disso, chame um método no cliente que exibe a mensagem de erro relevante.

public Task SampleMethod()
{
    try
    { 
        // code that can throw an exception
    }
    catch(Exception e)
    {
        // add code to log exception and take remedial steps

        return Clients.Caller.DisplayError("Sorry, the request could not be processed.");
    }
}