Compartir a través de


Introducción a la seguridad de SignalR

por Patrick Fletcher, Tom FitzMacken

Advertencia

Esta documentación no se aplica a la última versión de SignalR. Eche un vistazo a SignalR de ASP.NET Core.

En este artículo se describen los problemas de seguridad que debe tener en cuenta al desarrollar una aplicación SignalR.

Versiones de software empleadas en este tema

Versiones anteriores de este tema

Para obtener información sobre versiones anteriores de SignalR, consulte Versiones anteriores de SignalR.

Preguntas y comentarios

Deje sus comentarios sobre este tutorial y sobre lo que podríamos mejorar en los comentarios en la parte inferior de la página. Si tiene alguna pregunta que no esté directamente relacionadas con el tutorial, puede publicarla en el foro de ASP.NET SignalR o en StackOverflow.com.

Información general

Este documento contiene las siguientes secciones:

Conceptos de seguridad de SignalR

Autenticación y autorización

SignalR no proporciona ninguna característica para autenticar a los usuarios. En su lugar, integre las características de SignalR en la estructura de autenticación existente para una aplicación. En su lugar, se autentican a los usuarios como lo haría normalmente en su aplicación, y después se trabaja con los resultados de la autenticación en el código de SignalR. Por ejemplo, puede autenticar a sus usuarios con la autenticación de formularios ASP.NET y, después, en su centro de conectividad, imponer qué usuarios o roles están autorizados a llamar a un método. En su centro de conectividad, también puede pasar información de autenticación, como el nombre de usuario o si un usuario pertenece a un rol, al cliente.

SignalR proporciona el atributo Authorize para especificar qué usuarios tienen acceso a un centro de conectividad o a un método. Puede aplicar el atributo Authorize a un centro de conectividad o a determinados métodos de un centro de conectividad. Sin el atributo Authorize, todos los métodos públicos del centro de conectividad están disponibles para un cliente que esté conectado al mismo. Para más información sobre los centros de conectividad, consulte Autenticación y autorización de SignalR Hubs.

El atributo Authorize se aplica a los centros de conectividad, pero no a las conexiones persistentes. Para aplicar reglas de autorización al usar un PersistentConnection debe invalidar el método AuthorizeRequest. Para más información sobre las conexiones persistentes, consulte Autenticación y autorización para las conexiones persistentes de SignalR.

Token de conexión

SignalR mitiga el riesgo de ejecutar comandos maliciosos validando la identidad del remitente. Para cada solicitud, el cliente y el servidor pasan un token de conexión que contiene el identificador de conexión y el nombre de usuario para los usuarios autenticados. El identificador de conexión identifica de forma única cada cliente conectado. El servidor genera aleatoriamente el identificador de conexión cuando se crea una nueva conexión y conserva ese identificador durante la duración de la conexión. El mecanismo de autenticación de la aplicación web proporciona el nombre de usuario. SignalR usa el cifrado y una firma digital para proteger el token de conexión.

Diagram that shows an arrow from Client New Connection Request to Server Received Connection Request to Server Response to Client Received Response. The Authentication System generates a Connection Token in the Response and Received Response boxes.

En cada solicitud, el servidor valida el contenido del token para asegurarse de que la solicitud procede del usuario especificado. El nombre de usuario debe corresponder al id. de conexión. Al validar tanto el id. de conexión como el nombre de usuario, SignalR impide que un usuario malintencionado se haga pasar fácilmente por otro usuario. Si el servidor no puede validar el token de conexión, la solicitud falla.

Diagram that shows an arrow from Client Request to Server Received Request to Saved Token. Connection Token and Message are in both the Client box and the Server box.

Dado que el identificador de conexión forma parte del proceso de verificación, no debe revelar el identificador de conexión de un usuario a otros usuarios ni almacenar el valor en el cliente, como en una cookie.

Tokens de conexión frente a otros tipos de token

Las herramientas de seguridad marcan ocasionalmente los tokens de conexión porque parecen ser tokens de sesión o tokens de autenticación, lo que supone un riesgo si se expone.

El token de conexión de SignalR no es un token de autenticación. Se usa para confirmar que el usuario que realiza esta solicitud es el mismo que creó la conexión. El token de conexión es necesario porque ASP.NET SignalR permite que las conexiones se muevan entre servidores. El token asocia la conexión con un usuario determinado, pero no afirma la identidad del usuario que realiza la solicitud. Para que una solicitud de SignalR se autentique correctamente, debe tener algún otro token que afirme la identidad del usuario, como una cookie o un token de portador. Sin embargo, el propio token de conexión no notifica que el usuario haya realizado la solicitud, solo que el identificador de conexión contenido en el token está asociado a ese usuario.

Dado que el token de conexión no proporciona ninguna notificación de autenticación propia, no se considera un token de "sesión" o de "autenticación". Se producirá un error al tomar el token de conexión de un usuario determinado y reproducirlo en una solicitud autenticada como un usuario diferente (o una solicitud no autenticada), ya que la identidad del usuario de la solicitud y la identidad almacenada en el token no coincidirán.

Volver a unir grupos al volver a conectarse

De manera predeterminada, la aplicación SignalR reasignará automáticamente a un usuario a los grupos adecuados cuando se vuelva a conectar tras una interrupción temporal, como cuando se pierde una conexión y se vuelve a establecer antes de que se agote el tiempo de conexión. Al volver a conectarse, el cliente pasa un token de grupo que incluye el id. de conexión y los grupos asignados. El token de grupo está firmado digitalmente y cifrado. El cliente conserva el mismo id. de conexión después de una reconexión; por lo tanto, el id. de conexión que se pase desde el cliente reconectado debe coincidir con el id. de conexión anterior usado por el cliente. Esta verificación impide que un usuario malintencionado pase solicitudes para unirse a grupos no autorizados al volver a conectarse.

Sin embargo, es importante tener en cuenta que el token de grupo no expira. Si un usuario perteneció a un grupo en el pasado, pero se le prohibió la entrada a ese grupo, ese usuario puede ser capaz de imitar un token de grupo que incluya al grupo prohibido. Si necesita administrar de forma segura qué usuarios pertenecen a qué grupos, deberá almacenar esos datos en el servidor, por ejemplo en una base de datos. Después, agregue a su aplicación una lógica que verifique en el servidor si un usuario pertenece a un grupo. Para ver un ejemplo de verificación de la pertenencia a un grupo, consulte Trabajo con grupos.

La reincorporación automática a los grupos solo se aplica cuando se reconecta una conexión tras una interrupción temporal. Si un usuario se desconecta saliendo de la aplicación o la aplicación se reinicia, su aplicación debe controlar cómo agregar ese usuario a los grupos correctos. Para más información, consulte Trabajar con grupos.

Cómo evita SignalR la falsificación de solicitud entre sitios

La falsificación de solicitud entre sitios (CSRF) es un ataque en el que un sitio malicioso envía una solicitud a un sitio vulnerable en el que el usuario ha iniciado sesión. SignalR previene CSRF haciendo extremadamente improbable que un sitio malicioso cree una solicitud válida para su aplicación de SignalR.

Descripción del ataque CSRF

Este es un ejemplo de un ataque CSRF:

  1. Un usuario inicia sesión en www.example.com, mediante la autenticación de formularios.

  2. El servidor autentica al usuario. La respuesta del servidor incluye una cookie de autenticación.

  3. Sin cerrar sesión, el usuario visita un sitio web malintencionado. Este sitio malintencionado contiene el siguiente formato 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 la acción de formulario publica en el sitio vulnerable, no en el sitio malintencionado. Esta es la parte "entre sitios" de CSRF.

  4. El usuario hace clic en el botón Enviar. El explorador incluye la cookie de autenticación con la solicitud.

  5. La solicitud se ejecuta en el servidor de example.com con el contexto de autenticación del usuario, y puede hacer cualquier cosa que un usuario autenticado esté autorizado a hacer.

Aunque este ejemplo requiere que el usuario haga clic en el botón del formulario, la página maliciosa podría con la misma facilidad ejecutar un script que envíe una solicitud AJAX a su aplicación SignalR. Además, usar SSL no evita un ataque CSRF, porque el sitio malicioso puede enviar una solicitud "https://".

Normalmente, los ataques CSRF son posibles contra sitios web que usan cookies para la autenticación, ya que los exploradores envían todas las cookies pertinentes al sitio web de destino. Sin embargo, los ataques CSRF no se limitan a aprovechar las vulnerabilidades de las cookies. Por ejemplo, la autenticación básica e implícita también son vulnerables. Después de que un usuario inicie sesión con autenticación básica o autenticación implícita, el explorador envía automáticamente las credenciales hasta que finaliza la sesión.

Mitigación de CSRF adoptada por SignalR

SignalR toma las siguientes medidas para evitar que un sitio malicioso cree solicitudes válidas a su aplicación de SignalR. SignalR realiza estos pasos de forma predeterminada, no es necesario realizar ninguna acción en el código.

  • Deshabilitar solicitudes entre dominios SignalR deshabilita las solicitudes entre dominios para evitar que los usuarios llamen a un punto de conexión de SignalR desde un dominio externo. SignalR considera que cualquier solicitud de un dominio externo no es válida y bloquea la solicitud. Se recomienda mantener este comportamiento predeterminado; de lo contrario, un sitio malicioso podría engañar a los usuarios para que envíen comandos a su sitio. Si necesita usar solicitudes entre dominios, consulte Establecimiento de una conexión entre dominios.
  • Pasar el token de conexión en la cadena de consulta, no cookie SignalR pasa el token de conexión como un valor de cadena de consulta, en lugar de como una cookie. Almacenar el token de conexión en una cookie no es seguro porque el explorador puede reenviar accidentalmente el token de conexión cuando se encuentra código malintencionado. Además, pasar el token de conexión en la cadena de consulta impide que el token de conexión persista más allá de la conexión actual. Por lo tanto, un usuario malintencionado no puede realizar una solicitud con las credenciales de autenticación de otro usuario.
  • Comprobar token de conexión Como se describe en la sección Token de conexión, el servidor sabe qué identificador de conexión está asociado a cada usuario autenticado. El servidor no procesa ninguna solicitud procedente de un identificador de conexión que no coincida con el nombre de usuario. Es poco probable que un usuario malintencionado pueda adivinar una solicitud válida porque tendría que conocer el nombre de usuario y el id. de conexión actual generado aleatoriamente. Ese id. de conexión deja de ser válido en cuanto finaliza la conexión. Los usuarios anónimos no deberían tener acceso a ninguna información confidencial.

Recomendaciones de seguridad de SignalR

Protocolo de Capas de sockets seguros (SSL)

El protocolo SSL usa el cifrado para proteger el transporte de datos entre un cliente y un servidor. Si su aplicación de SignalR transmite información confidencial entre el cliente y el servidor, use SSL para el transporte. Para más información sobre cómo configurar SSL, consulte Configuración de SSL en IIS 7.

No use grupos como mecanismo de seguridad

Los grupos son una forma cómoda de agrupar a los usuarios relacionados, pero no son un mecanismo seguro para limitar el acceso a la información confidencial. Esto es especialmente cierto cuando los usuarios pueden volver a unirse automáticamente a los grupos durante una reconexión. En su lugar, considere la posibilidad de añadir usuarios con privilegios a un rol y limitar el acceso a un método de centro de conectividad solo a los miembros de ese rol. Para ver un ejemplo de restricción de acceso basada en un rol, consulte Autenticación y autorización de SignalR Hubs. Para ver un ejemplo de comprobación del acceso del usuario a los grupos al volver a conectarse, consulte Trabajar con grupos.

Control seguro de las entradas de los clientes

Para asegurar que un usuario malintencionado no envíe scripts a otros usuarios, deben codificarse todas las entradas de los clientes destinadas a su difusión a otros clientes. Debe codificar los mensajes en los clientes receptores que en el servidor, porque su aplicación de SignalR puede tener muchos tipos diferentes de clientes. Por lo tanto, la codificación HTML funciona para un cliente web, pero no para otros tipos de clientes. Por ejemplo, un método de cliente web para mostrar un mensaje de chat controlaría con seguridad el nombre del usuario y el mensaje llamando a la función html().

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>');
};

Reconciliación de un cambio en el estado del usuario con una conexión activa

Si el estado de autenticación de un usuario cambia mientras existe una conexión activa, el usuario recibirá un error que dice: "La identidad del usuario no puede cambiar durante una conexión de SignalR activa." En ese caso, su aplicación debería volver a conectarse al servidor para asegurarse de que el id. de conexión y el nombre de usuario están coordinados. Por ejemplo, si su aplicación permite al usuario desconectarse mientras existe una conexión activa, el nombre de usuario de la conexión ya no coincidirá con el nombre que se pase para la siguiente solicitud. Querrá detener la conexión antes de que el usuario cierre la sesión y después reiniciarla.

Sin embargo, es importante tener en cuenta que la mayoría de las aplicaciones no necesitarán detener e iniciar manualmente la conexión. Si su aplicación redirige a los usuarios a una página distinta después de cerrar la sesión, como el comportamiento predeterminado en una aplicación de Web Forms o una aplicación de MVC, o actualiza la página actual después de cerrar la sesión, la conexión activa se desconecta automáticamente y no requiere ninguna acción adicional.

El siguiente ejemplo muestra cómo detener e iniciar una conexión cuando ha cambiado el estado del usuario.

<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>

O bien, el estado de autenticación del usuario puede cambiar si su sitio usa la expiración variable con la autenticación de formularios, y no hay actividad para mantener válida la cookie de autenticación. En ese caso, se cerrará la sesión del usuario y su nombre dejará de coincidir con el nombre de usuario que figura en el token de conexión. Puede solucionar este problema añadiendo algún script que solicite periódicamente un recurso en el servidor web para mantener válida la cookie de autenticación. El siguiente ejemplo muestra cómo solicitar un recurso cada 30 minutos.

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

Archivos proxy de JavaScript generados automáticamente

Si no quiere incluir todos los centros de conectividad y métodos en el archivo proxy de JavaScript para cada usuario, puede deshabilitar la generación automática del archivo. Puede elegir esta opción si tiene varios centros de conectividad y métodos, pero no quiere que todos los usuarios conozcan todos los métodos. Para deshabilitar la generación automática, establezca EnableJavaScriptProxies en false.

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

Para más información sobre los archivos proxy de JavaScript, consulte El proxy generado y lo que hace por usted.

Excepciones

Debe evitar pasar objetos de excepción a los clientes porque los objetos pueden exponer información confidencial a los clientes. En su lugar, llame a un método en el cliente que muestre el mensaje de error correspondiente.

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.");
    }
}