Introduction à la sécurité de SignalR
par Patrick Fletcher, Tom FitzMacken
Avertissement
Cette documentation ne concerne pas la dernière version de SignalR. Consultez 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.
Versions logicielles utilisées dans cette rubrique
- Visual Studio 2013
- .NET 4.5
- SignalR version 2
Versions précédentes de cette rubrique
Pour plus d’informations sur les versions antérieures de SignalR, consultez Anciennes versions de SignalR.
Questions et commentaires
Laissez vos commentaires sur la façon dont vous avez aimé ce tutoriel et sur ce que nous pourrions améliorer dans les commentaires en bas de la page. Si vous avez des questions qui ne sont pas directement liées au tutoriel, vous pouvez les publier sur le forum ASP.NET SignalR ou StackOverflow.com.
Vue d’ensemble
Ce document contient les sections suivantes :
Concepts de sécurité signalr
Authentification et autorisation
SignalR ne fournit aucune fonctionnalité pour l’authentification des utilisateurs. Au lieu de cela, vous intégrez les fonctionnalités SignalR à la structure d’authentification existante pour une application. Vous authentifiez les utilisateurs comme vous le feriez normalement dans votre application et 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 SignalR Hubs.
Vous appliquez l’attribut Authorize
aux hubs, mais pas aux connexions persistantes. 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. Pour chaque requête, le client et le serveur passent un jeton de connexion qui contient l’ID de connexion et le nom d’utilisateur des utilisateurs authentifiés. L’ID de connexion identifie de manière unique chaque client connecté. Le serveur génère de manière aléatoire l’ID de connexion lors de la création d’une nouvelle connexion et conserve cet ID pendant la durée de la connexion. Le mécanisme d’authentification de l’application web fournit le nom d’utilisateur. SignalR utilise le chiffrement et une signature numérique pour protéger 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.
É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 ni stocker la valeur sur le client, par exemple dans un cookie.
Jetons de connexion et autres types de jetons
Les jetons de connexion sont parfois marqués par les outils de sécurité, car ils semblent être des jetons de session ou des jetons d’authentification, ce qui présente un risque s’ils sont exposés.
Le jeton de connexion de SignalR n’est pas un jeton d’authentification. Il est utilisé pour vérifier que l’utilisateur qui effectue cette demande est le même que celui qui a créé la connexion. Le jeton de connexion est nécessaire, car ASP.NET SignalR permet aux connexions de se déplacer entre les serveurs. Le jeton associe la connexion à un utilisateur particulier, mais n’affirme pas l’identité de l’utilisateur qui effectue la demande. Pour qu’une demande SignalR soit correctement authentifiée, elle doit avoir un autre jeton qui affirme l’identité de l’utilisateur, comme un cookie ou un jeton du porteur. Toutefois, le jeton de connexion lui-même ne prétend pas que la demande a été effectuée par cet utilisateur, mais seulement que l’ID de connexion contenu dans le jeton est associé à cet utilisateur.
Étant donné que le jeton de connexion ne fournit aucune revendication d’authentification propre, il n’est pas considéré comme un jeton de « session » ou « d’authentification ». L’obtention du jeton de connexion d’un utilisateur donné et sa relecture dans une requête authentifiée en tant qu’utilisateur différent (ou une demande non authentifiée) échouent, car l’identité de l’utilisateur de la demande et l’identité stockée dans le jeton ne correspondent pas.
Rejoindre des groupes lors de la reconnexion
Par défaut, l’application SignalR réapplie 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 attribué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, cet utilisateur peut être en mesure d’imiter un jeton de groupe qui inclut le groupe interdit. Si vous devez gérer de manière sécurisée 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 des groupes.
Comment SignalR empêche la falsification de requête intersite
La falsification de requête intersite (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 peu probable pour un site malveillant de créer une demande valide pour votre application SignalR.
Description de l’attaque CSRF
Voici un exemple d’attaque CSRF :
Un utilisateur se connecte à www.example.com à l’aide de l’authentification par formulaire.
Le serveur authentifie l’utilisateur. La réponse du serveur inclut un cookie d’authentification.
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 est postées sur le site vulnérable, et non sur le site malveillant. Il s’agit de la partie « intersite » de CSRF.
L’utilisateur clique sur le bouton Envoyer. Le navigateur inclut le cookie d’authentification avec la demande.
La demande s’exécute sur le serveur example.com avec le contexte d’authentification de l’utilisateur et peut faire 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 facilement exécuter un script qui envoie une requête AJAX à votre application SignalR. De plus, 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 demandes valides pour votre application. SignalR effectue ces étapes par défaut. Vous n’avez pas besoin d’effectuer d’action dans votre code.
- Désactiver les demandes inter-domaines SignalR désactive les demandes inter-domaines pour empêcher les utilisateurs d’appeler un point de terminaison SignalR à partir d’un domaine externe. SignalR considère que toute requête provenant d’un domaine externe n’est pas valide et bloque la demande. Nous vous recommandons 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. Le stockage du jeton de connexion dans un cookie est dangereux, car le navigateur peut transférer par inadvertance le jeton de connexion en cas de présence de code malveillant. En outre, le passage du jeton de connexion dans la chaîne de requête empêche le jeton de connexion de persister 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
Pour vous assurer qu’un utilisateur malveillant n’envoie pas de script à d’autres utilisateurs, vous devez encoder toutes les entrées des clients destinées à être diffusées vers d’autres clients. Vous devez 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;
app.MapSignalR(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.");
}
}