Introduction à la sécurité de SignalR (SignalR 1.x)

par Patrick Fletcher, Tom FitzMacken

Avertissement

Cette documentation ne concerne pas la dernière version de SignalR. Jetez un coup d’œil à ASP.NET Core SignalR.

Cet article décrit les problèmes de sécurité que vous devez prendre en compte lors du développement d’une application SignalR.

Vue d’ensemble

Ce document contient les sections suivantes :

Concepts de sécurité signalR

Authentification et autorisation

SignalR est conçu pour être intégré à la structure d’authentification existante pour une application. Il ne fournit aucune fonctionnalité permettant d’authentifier les utilisateurs. Au lieu de cela, vous authentifiez les utilisateurs comme vous le feriez normalement dans votre application, puis vous utilisez les résultats de l’authentification dans votre code SignalR. Par exemple, vous pouvez authentifier vos utilisateurs avec ASP.NET l’authentification par formulaire, puis dans votre hub, appliquer quels utilisateurs ou rôles sont autorisés à appeler une méthode. Dans votre hub, vous pouvez également transmettre des informations d’authentification, telles que le nom d’utilisateur ou l’appartenance d’un utilisateur à un rôle, au client.

SignalR fournit l’attribut Authorize pour spécifier les utilisateurs qui ont accès à un hub ou à une méthode. Vous appliquez l’attribut Authorize à un hub ou à des méthodes particulières dans un hub. Sans l’attribut Authorize, toutes les méthodes publiques sur le hub sont disponibles pour un client connecté au hub. Pour plus d’informations sur les hubs, consultez Authentification et autorisation pour Les hubs SignalR.

L’attribut Authorize est utilisé uniquement avec les hubs. Pour appliquer des règles d’autorisation lors de l’utilisation d’un, PersistentConnection vous devez remplacer la AuthorizeRequest méthode . Pour plus d’informations sur les connexions persistantes, consultez Authentification et autorisation pour les connexions persistantes SignalR.

Jeton de connexion

SignalR atténue le risque d’exécution de commandes malveillantes en validant l’identité de l’expéditeur. Un jeton de connexion, contenant l’ID de connexion et le nom d’utilisateur pour les utilisateurs authentifiés, est passé entre le client et le serveur pour chaque requête. L’ID de connexion est un identificateur unique généré de manière aléatoire par le serveur lorsqu’une nouvelle connexion est créée et conservé pendant la durée de la connexion. Le nom d’utilisateur est fourni par le mécanisme d’authentification de l’application web. Le jeton de connexion est protégé par un chiffrement et une signature numérique.

Diagramme système de jeton de connexion, montrant la relation entre le client, le serveur, le système d’authentification et le jeton de connexion.

Pour chaque requête, le serveur valide le contenu du jeton pour s’assurer que la demande provient de l’utilisateur spécifié. Le nom d’utilisateur doit correspondre à l’ID de connexion. En validant à la fois l’ID de connexion et le nom d’utilisateur, SignalR empêche un utilisateur malveillant d’emprunter facilement l’identité d’un autre utilisateur. Si le serveur ne peut pas valider le jeton de connexion, la demande échoue.

Diagramme du système de jeton de connexion, montrant la relation entre le client, le serveur et le jeton enregistré.

Étant donné que l’ID de connexion fait partie du processus de vérification, vous ne devez pas révéler l’ID de connexion d’un utilisateur à d’autres utilisateurs ou stocker la valeur sur le client, par exemple dans un cookie.

Rejoindre des groupes lors de la reconnexion

Par défaut, l’application SignalR réinscrit automatiquement un utilisateur aux groupes appropriés lors de la reconnexion après une interruption temporaire, par exemple lorsqu’une connexion est supprimée et rétablie avant l’expiration de la connexion. Lors de la reconnexion, le client transmet un jeton de groupe qui inclut l’ID de connexion et les groupes affectés. Le jeton de groupe est signé et chiffré numériquement. Le client conserve le même ID de connexion après une reconnexion ; Par conséquent, l’ID de connexion transmis à partir du client reconnecté doit correspondre à l’ID de connexion précédent utilisé par le client. Cette vérification empêche un utilisateur malveillant de transmettre des demandes pour rejoindre des groupes non autorisés lors de la reconnexion.

Toutefois, il est important de noter que le jeton de groupe n’expire pas. Si un utilisateur appartenait à un groupe dans le passé, mais qu’il a été interdit de ce groupe, il peut être en mesure d’imiter un jeton de groupe qui inclut le groupe interdit. Si vous devez gérer en toute sécurité quels utilisateurs appartiennent à quels groupes, vous devez stocker ces données sur le serveur, par exemple dans une base de données. Ensuite, ajoutez une logique à votre application qui vérifie sur le serveur si un utilisateur appartient à un groupe. Pour obtenir un exemple de vérification de l’appartenance à un groupe, consultez Utilisation de groupes.

La réintégration automatique des groupes s’applique uniquement lorsqu’une connexion est reconnectée après une interruption temporaire. Si un utilisateur se déconnecte en s’éloignant de l’application ou si l’application redémarre, votre application doit gérer la façon d’ajouter cet utilisateur aux groupes appropriés. Pour plus d’informations, consultez Utilisation de groupes.

Comment SignalR empêche la falsification de requête intersites

La falsification de requête intersites (CSRF) est une attaque par laquelle un site malveillant envoie une requête à un site vulnérable où l’utilisateur est actuellement connecté. SignalR empêche CSRF en rendant extrêmement improbable la création d’une demande valide pour votre application SignalR par un site malveillant.

Description de l’attaque CSRF

Voici un exemple d’attaque CSRF :

  1. Un utilisateur se connecte à www.example.com, à l’aide de l’authentification par formulaire.

  2. Le serveur authentifie l’utilisateur. La réponse du serveur inclut un cookie d’authentification.

  3. Sans se déconnecter, l’utilisateur visite un site web malveillant. Ce site malveillant contient le formulaire HTML suivant :

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

    Notez que l’action de formulaire s’affiche sur le site vulnérable, et non sur le site malveillant. Il s’agit de la partie « intersites » de CSRF.

  4. L’utilisateur clique sur le bouton Envoyer. Le navigateur inclut le cookie d’authentification avec la demande.

  5. La demande s’exécute sur le serveur example.com avec le contexte d’authentification de l’utilisateur et peut effectuer tout ce qu’un utilisateur authentifié est autorisé à faire.

Bien que cet exemple exige que l’utilisateur clique sur le bouton de formulaire, la page malveillante peut tout aussi bien exécuter un script qui envoie une requête AJAX à votre application SignalR. En outre, l’utilisation de SSL n’empêche pas une attaque CSRF, car le site malveillant peut envoyer une requête « https:// ».

En règle générale, les attaques CSRF sont possibles contre les sites web qui utilisent des cookies pour l’authentification, car les navigateurs envoient tous les cookies pertinents au site web de destination. Toutefois, les attaques CSRF ne se limitent pas à l’exploitation des cookies. Par exemple, l’authentification de base et l’authentification Digest sont également vulnérables. Une fois qu’un utilisateur s’est connecté avec l’authentification De base ou Digest, le navigateur envoie automatiquement les informations d’identification jusqu’à la fin de la session.

Atténuations CSRF prises par SignalR

SignalR effectue les étapes suivantes pour empêcher un site malveillant de créer des requêtes valides pour votre application SignalR. Ces étapes sont effectuées par défaut et ne nécessitent aucune action dans votre code.

  • Désactiver les demandes inter-domaines
    Par défaut, les requêtes inter-domaines sont désactivées dans une application SignalR pour empêcher les utilisateurs d’appeler un point de terminaison SignalR à partir d’un domaine externe. Toute requête provenant d’un domaine externe est automatiquement considérée comme non valide et bloquée. Il est recommandé de conserver ce comportement par défaut ; dans le cas contraire, un site malveillant pourrait inciter les utilisateurs à envoyer des commandes à votre site. Si vous avez besoin d’utiliser des requêtes inter-domaines, consultez Comment établir une connexion inter-domaines .
  • Transmettre le jeton de connexion dans la chaîne de requête, et non le cookie
    SignalR transmet le jeton de connexion en tant que valeur de chaîne de requête, plutôt qu’en tant que cookie. En ne stockant pas le jeton de connexion en tant que cookie, le jeton de connexion n’est pas transféré par inadvertance par le navigateur lorsque du code malveillant est rencontré. En outre, le jeton de connexion n’est pas conservé au-delà de la connexion actuelle. Par conséquent, un utilisateur malveillant ne peut pas effectuer une demande sous les informations d’identification d’authentification d’un autre utilisateur.
  • Vérifier le jeton de connexion
    Comme décrit dans la section Jeton de connexion , le serveur sait quel ID de connexion est associé à chaque utilisateur authentifié. Le serveur ne traite aucune demande provenant d’un ID de connexion qui ne correspond pas au nom d’utilisateur. Il est peu probable qu’un utilisateur malveillant puisse deviner une demande valide, car l’utilisateur malveillant doit connaître le nom d’utilisateur et l’ID de connexion généré de manière aléatoire actuelle. Cet ID de connexion devient non valide dès que la connexion est terminée. Les utilisateurs anonymes ne doivent pas avoir accès à des informations sensibles.

Recommandations de sécurité SignalR

Protocole SSL (Secure Socket Layer)

Le protocole SSL utilise le chiffrement pour sécuriser le transport des données entre un client et un serveur. Si votre application SignalR transmet des informations sensibles entre le client et le serveur, utilisez SSL pour le transport. Pour plus d’informations sur la configuration de SSL, consultez Comment configurer SSL sur IIS 7.

Ne pas utiliser de groupes comme mécanisme de sécurité

Les groupes sont un moyen pratique de collecter des utilisateurs associés, mais ils ne constituent pas un mécanisme sécurisé pour limiter l’accès aux informations sensibles. Cela est particulièrement vrai lorsque les utilisateurs peuvent automatiquement rejoindre des groupes pendant une reconnexion. Au lieu de cela, envisagez d’ajouter des utilisateurs privilégiés à un rôle et de limiter l’accès à une méthode hub aux seuls membres de ce rôle. Pour obtenir un exemple de restriction de l’accès en fonction d’un rôle, consultez Authentification et autorisation pour SignalR Hubs. Pour obtenir un exemple de vérification de l’accès utilisateur aux groupes lors de la reconnexion, consultez Utilisation des groupes.

Gestion sécurisée des entrées des clients

Toutes les entrées des clients destinées à être diffusées vers d’autres clients doivent être encodées pour garantir qu’un utilisateur malveillant n’envoie pas de script à d’autres utilisateurs. Il est préférable d’encoder les messages sur les clients de réception plutôt que sur le serveur, car votre application SignalR peut avoir de nombreux types de clients différents. Par conséquent, l’encodage HTML fonctionne pour un client web, mais pas pour d’autres types de clients. Par exemple, une méthode cliente web pour afficher un message de conversation gère en toute sécurité le nom d’utilisateur et le message en appelant la html() fonction .

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

Rapprochement d’un changement de status utilisateur avec une connexion active

Si le status d’authentification d’un utilisateur change alors qu’une connexion active existe, l’utilisateur reçoit une erreur indiquant : « L’identité de l’utilisateur ne peut pas être modifiée pendant une connexion SignalR active ». Dans ce cas, votre application doit se reconnecter au serveur pour s’assurer que l’ID de connexion et le nom d’utilisateur sont coordonnés. Par exemple, si votre application permet à l’utilisateur de se déconnecter pendant qu’une connexion active existe, le nom d’utilisateur de la connexion ne correspond plus au nom passé pour la requête suivante. Vous devez arrêter la connexion avant que l’utilisateur se déconnecte, puis la redémarrer.

Toutefois, il est important de noter que la plupart des applications n’auront pas besoin d’arrêter et de démarrer manuellement la connexion. Si votre application redirige les utilisateurs vers une page distincte après la déconnexion, par exemple le comportement par défaut dans une application Web Forms ou MVC, ou actualise la page active après s’être déconnectée, la connexion active est automatiquement déconnectée et ne nécessite aucune action supplémentaire.

L’exemple suivant montre comment arrêter et démarrer une connexion lorsque l’utilisateur status a changé.

<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 bien, le status d’authentification de l’utilisateur peut changer si votre site utilise l’expiration glissante avec l’authentification par formulaire, et qu’il n’existe aucune activité permettant de conserver le cookie d’authentification valide. Dans ce cas, l’utilisateur est déconnecté et le nom d’utilisateur ne correspond plus au nom d’utilisateur dans le jeton de connexion. Vous pouvez résoudre ce problème en ajoutant un script qui demande régulièrement une ressource sur le serveur web pour que le cookie d’authentification reste valide. L’exemple suivant montre comment demander une ressource toutes les 30 minutes.

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

Fichiers proxy JavaScript générés automatiquement

Si vous ne souhaitez pas inclure tous les hubs et méthodes dans le fichier proxy JavaScript pour chaque utilisateur, vous pouvez désactiver la génération automatique du fichier. Vous pouvez choisir cette option si vous avez plusieurs hubs et méthodes, mais que vous ne souhaitez pas que tous les utilisateurs soient conscients de toutes les méthodes. Vous désactivez la génération automatique en définissant EnableJavaScriptProxies sur false.

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableJavaScriptProxies = false;
RouteTable.Routes.MapHubs("/signalr", hubConfiguration);

Pour plus d’informations sur les fichiers proxy JavaScript, consultez Le proxy généré et ce qu’il fait pour vous.

Exceptions

Vous devez éviter de transmettre des objets d’exception aux clients, car les objets peuvent exposer des informations sensibles aux clients. Au lieu de cela, appelez une méthode sur le client qui affiche le message d’erreur approprié.

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