Conseils d’atténuation des menaces pour le rendu interactif côté serveur de ASP.NET Core Blazor
Remarque
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 8 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la Stratégie de prise en charge de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 8 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 8 de cet article.
Cet article explique comment atténuer les menaces de sécurité dans les applications Blazor.
Les applications adoptent un modèle de traitement des données avec état, dans lequel le serveur et le client maintiennent une relation de longue durée. L’état persistant est maintenu par un circuit, qui peut s’étendre sur des connexions potentiellement de longue durée.
Lorsqu’un utilisateur visite un site , le serveur crée un circuit dans la mémoire du serveur. Le circuit indique au navigateur le contenu à afficher et répond aux événements, par exemple lorsque l’utilisateur sélectionne un bouton dans l’interface utilisateur. Pour effectuer ces actions, un circuit appelle des fonctions JavaScript dans le navigateur de l’utilisateur et des méthodes .NET sur le serveur. Cette interaction bidirectionnelle basée sur JavaScript est appelée interopérabilité JavaScript (interop JS).
Étant donné que l’interopérabilité de JS se produit sur Internet et que le client utilise un navigateur distant, les applications partagent la plupart des problèmes de sécurité des applications web. Cette rubrique décrit les menaces courantes pour les applications Blazor côté serveur et fournit des conseils d’atténuation des menaces visant les applications pour Internet.
Dans les environnements contraints, comme à l’intérieur de réseaux d’entreprise ou d’intranets, certains conseils d’atténuation :
- Ne s’appliquent pas dans l’environnement contraint.
- Ne valent pas le coût de l’implémentation, car le risque de sécurité est faible dans un environnement contraint.
Composants serveur interactifs avec compression WebSocket activée
La compression peut exposer l’application à des attaques par canal auxiliaire contre le chiffrement TLS de la connexion, comme les attaques CRIME et BREACH. Ces types d’attaques nécessitent que le cyberattaquant :
- Force un navigateur à émettre des requêtes avec une charge utile que le cyberattaquant contrôle sur un site vulnérable via une publication de formulaire intersites ou en incorporant le site à l’intérieur d’un iframe d’un autre site.
- Observe la longueur de la réponse compressée et chiffrée sur le réseau.
Pour que l’application soit vulnérable, elle doit refléter la charge utile du cyberattaquant dans la réponse, par exemple, en écrivant le chemin d’accès ou la chaîne de requête dans la réponse. À partir de la longueur de la réponse, le cyberattaquant peut « deviner » toutes les informations sur la réponse, contournant ainsi le chiffrement de la connexion.
En règle générale, les applications Blazor peuvent activer la compression sur la connexion WebSocket avec les mesures de sécurité appropriées :
L’application peut être vulnérable lorsqu’elle prend du contenu de la requête (par exemple, le chemin d’accès ou la chaîne de requête) qui peut être influencé par un cyberattaquant, et qu’elle le reproduit dans le code HTML de la page ou l’utilise comme partie de la réponse.
Blazor applique automatiquement les mesures de sécurité suivantes :
Lorsque la compression est configurée, Blazor bloque automatiquement l’incorporation de l’application dans un iframe, ce qui empêche le rendu de la réponse initiale (non compressée) du serveur et empêche la connexion WebSocket de démarrer.
La restriction sur l’incorporation de l’application dans un iframe peut être assouplie. Toutefois, l’assouplissement de cette restriction expose l’application à une attaque si le document réalisant l’incorporation est compromis par une vulnérabilité de scripting inter-site, car cela fournit au cyberattaquant un moyen d’exécuter l’attaque.
Normalement, pour que ce type d’attaque se produise, l’application doit reproduire à plusieurs reprises le contenu dans les réponses pour que le cyberttaquant puisse deviner la réponse. Vu la méthode de rendu de Blazor (le rendu s’effectue une seule fois, et par la suite les différences dans le contenu sont uniquement générées pour les éléments qui ont changé), il est difficile pour un cyberattaquant d’y parvenir. Cela n’est toutefois pas impossible pour un cyberattaquant, il est donc important de faire preuve de prudence et d’éviter d’effectuer le rendu d’informations sensibles en même temps que des informations externes qui peuvent être manipulées par un cyberattaquant. Quelques exemples :
Affichez les informations d’identification personnelle (PII) sur la page en même temps que le rendu des données de base de données ajoutées par un autre utilisateur.
Rendre les PII sur la page en même temps que les données provenant d’un autre utilisateur via l’interopérabilité JS ou un service singleton local sur le serveur.
En général, nous vous recommandons d’éviter de rendre des composants qui contiennent des informations sensibles en même temps que des composants qui peuvent restituer des données à partir de sources non approuvées dans le cadre du même lot de rendu. Les sources non approuvées incluent les paramètres de routage, les chaînes de requête, les données de JS l’interopérabilité, et toute autre source de données qu’un utilisateur tiers peut contrôler (bases de données, services externes).
État partagé
Les applications Blazor côté serveur résident dans la mémoire du serveur, et plusieurs sessions d’application sont hébergées dans le même processus. Pour chaque session d’application, Blazor démarre un circuit avec sa propre étendue de conteneur d’injection de dépendances, de sorte que les services délimités sont uniques par session Blazor.
Avertissement
Nous ne recommandons pas que les applications sur le même serveur partagent un état à l’aide des services singleton, sauf si des précautions extrêmes sont prises, car cela peut introduire des vulnérabilités de sécurité, comme des fuites d’état utilisateur entre les circuits.
Vous pouvez utiliser des services singleton avec état dans les applications Blazor si elles sont spécifiquement conçues pour cela. Par exemple, l’utilisation d’un cache de mémoire singleton est acceptable, car un cache de mémoire nécessite une clé pour accéder à une entrée donnée. En supposant que les utilisateurs ne contrôlent pas les clés de cache utilisées avec le cache, l’état stocké dans le cache ne fuit pas entre les circuits.
Pour obtenir des conseils généraux sur la gestion de l’état, consultez Gestion de l’état BlazorASP.NET Core.
IHttpContextAccessor
/HttpContext
dans les composants Razor
IHttpContextAccessor doit être évité avec le rendu interactif, car il n’existe pas de HttpContext
valide disponible.
IHttpContextAccessor peut être utilisé pour les composants rendus statiquement sur le serveur. Toutefois, nous vous recommandons de l’éviter si possible.
HttpContext peut être utilisé comme paramètre en cascade uniquement dans les composants racines rendus statiquement pour les tâches générales, telles que l’inspection et la modification d’en-têtes ou d’autres propriétés dans le composant App
(Components/App.razor
). La valeur est toujours null
pour le rendu interactif.
[CascadingParameter]
public HttpContext? HttpContext { get; set; }
Pour les scénarios où HttpContext est requis dans les composants interactifs, nous vous recommandons de transmettre les données via l’état du composant persistant à partir du serveur. Pour plus d’informations, consultez Autres scénarios de sécurité ASP.NET Core Blazor côté serveur.
N’utilisez pas IHttpContextAccessor/HttpContext directement ou indirectement dans les composants Razor des applications Blazor côté serveur. Les applications Blazor s’exécutent en dehors du contexte de pipeline ASP.NET Core. Le HttpContext n’est pas garanti d’être disponible dans le IHttpContextAccessor, et HttpContext n’est pas garanti de conserver le contexte qui a démarré l’application Blazor.
L’approche recommandée pour passer l’état de la requête à l’application Blazor consiste à utiliser les paramètres de composant racine pendant le rendu initial de l’application. L’application peut également copier les données dans un service délimité dans l’événement de cycle de vie d’initialisation du composant racine pour une utilisation dans l’application. Pour plus d’informations, consultez Autres scénarios de sécurité ASP.NET Core Blazor côté serveur.
Un aspect essentiel de la sécurité de Blazor côté serveur est que l’utilisateur attaché à un circuit donné peut être mis à jour à un moment donné après l’établissement du circuit Blazor, mais que le IHttpContextAccessor n’est pas mis à jour. Pour plus d’informations sur la résolution de cette situation avec des services personnalisés, consultez Autres scénarios de sécurité ASP.NET Core Blazor côté serveur.
Épuisement des ressources
L’épuisement des ressources peut se produire lorsqu’un client interagit avec le serveur et que celui-ci consomme des ressources excessives. La consommation excessive de ressources affecte principalement :
Les attaques par déni de service (DoS) visent généralement à épuiser les ressources d’une application ou d’un serveur. Toutefois, l’épuisement des ressources n’est pas nécessairement le résultat d’une attaque sur le système. Par exemple, les ressources limitées peuvent être épuisées en raison d’une demande élevée des utilisateurs. Les attaques DoS sont abordées plus loin dans la section DoS.
Les ressources externes au framework Blazor, comme les bases de données et les descripteurs de fichiers (utilisés pour lire et écrire des fichiers), peuvent également rencontrer un épuisement des ressources. Pour plus d’informations, consultez Meilleures pratiques pour ASP.NET Core.
UC
L’épuisement du processeur peut se produire lorsqu’un ou plusieurs clients forcent le serveur à effectuer un travail intensif pour le processeur.
Par exemple, considérez une application qui calcule une suite de Fibonnacci. Un nombre de Fibonnacci est produit à partir d’une séquence de Fibonnacci, où chaque nombre de la séquence est la somme des deux nombres précédents. La quantité de travail nécessaire pour atteindre la réponse dépend de la longueur de la séquence et de la taille de la valeur initiale. Si l’application n’impose pas de limites à la requête d’un client, les calculs gourmands en ressources processeur peuvent dominer le temps processeur et diminuer les performances des autres tâches. La consommation excessive de ressources est un problème de sécurité qui a un impact sur la disponibilité.
L’épuisement du processeur est une préoccupation pour toutes les applications publiques. Dans les applications web standard, les requêtes et les connexions expirent à titre de protection, mais les applications Blazor ne fournissent pas les mêmes sécurités. Les applications Blazor doivent inclure des vérifications et des limites appropriées avant d’effectuer un travail potentiellement gourmand en ressources processeur.
Mémoire
L’épuisement de la mémoire peut se produire lorsqu’un ou plusieurs clients forcent le serveur à consommer une grande quantité de mémoire.
Par exemple, considérez une application avec un composant qui accepte et affiche une liste d’éléments. Si l’application Blazor n’impose pas de limites au nombre d’éléments autorisés ou au nombre d’éléments rendus au client, le traitement et le rendu, nécessitant beaucoup de mémoire, peuvent dominer la mémoire du serveur au point que les performances du serveur en souffrent. Le serveur peut se bloquer ou ralentir au point qu’il semble être en panne.
Envisagez le scénario suivant pour la maintenance et l’affichage d’une liste d’éléments qui se rapportent à un scénario d’épuisement potentiel de la mémoire sur le serveur :
- Les éléments d’une propriété ou d’un champ
List<T>
utilisent la mémoire du serveur. Si l’application permet à la liste d’éléments de s’étendre sans limite, le serveur risque de manquer de mémoire. Le manque de mémoire entraîne la fin de la session active (blocage), et toutes les sessions simultanées de cette instance de serveur reçoivent une exception de mémoire insuffisante. Pour éviter ce scénario, l’application doit utiliser une structure de données qui impose une limite d’éléments aux utilisateurs simultanés. - Si un schéma de pagination n’est pas utilisé pour le rendu, le serveur utilise de la mémoire supplémentaire pour les objets qui ne sont pas visibles dans l’interface utilisateur. Sans limitation du nombre d’éléments, les besoins en mémoire peuvent épuiser la mémoire du serveur disponible. Pour éviter ce scénario, utilisez l’une des approches suivantes :
- Utilisez des listes paginées lors du rendu.
- Affichez uniquement les 100 à 1 000 premiers éléments et exigez que l’utilisateur entre des critères de recherche pour rechercher des éléments au-delà des éléments affichés.
- Pour un scénario de rendu plus avancé, implémentez des listes ou des grilles qui prennent en charge la virtualisation. À l’aide de la virtualisation, les listes affichent uniquement un sous-ensemble d’éléments actuellement visibles par l’utilisateur. Lorsque l’utilisateur interagit avec la barre de défilement dans l’interface utilisateur, le composant affiche uniquement les éléments nécessaires à l’affichage. Les éléments qui ne sont actuellement pas requis pour l’affichage peuvent être conservés dans le stockage secondaire, ce qui est l’approche idéale. Les éléments non affichés peuvent également être conservés en mémoire, ce qui est moins idéal.
Note
Blazor prend en charge la virtualisation intégrée. Pour plus d’informations, consultez Virtualisation des composants ASP.NET Core Razor.
Les applications Blazor offrent un modèle de programmation similaire à d’autres infrastructures d’interface utilisateur pour les applications avec état, comme WPF, Windows Forms ou Blazor WebAssembly. La différence principale est qu’avec plusieurs infrastructures d’interface utilisateur, la mémoire consommée par l’application appartient au client et affecte uniquement ce client individuel. Par exemple, une application Blazor WebAssembly s’exécute entièrement sur le client et utilise uniquement les ressources de mémoire du client. Pour une application Blazor côté serveur, la mémoire consommée par l’application appartient au serveur et est partagée entre les clients sur l’instance du serveur.
Les besoins en mémoire côté serveur sont à prendre en compte pour toutes les applications Blazor côté serveur. Toutefois, la plupart des applications web sont sans état et la mémoire utilisée lors du traitement d’une requête est libérée lorsque la réponse est retournée. En règle générale, n’autorisez pas les clients à allouer une quantité de mémoire non limitée comme dans toute autre application côté serveur qui conserve les connexions clientes. La mémoire consommée par une application Blazor côté serveur persiste plus longtemps qu’une requête unique.
Remarque
Pendant le développement, un profileur peut être utilisé, ou une trace peut être capturée pour évaluer les besoins en mémoire des clients. Un profileur ou une trace ne capture pas la mémoire allouée à un client spécifique. Pour capturer l’utilisation de la mémoire d’un client spécifique pendant le développement, capturez un vidage et examinez la demande de mémoire de tous les objets enracinés sur le circuit d’un utilisateur.
Connexions clientes
L’épuisement de connexions peut se produire lorsqu’un ou plusieurs clients ouvrent trop de connexions simultanées au serveur, empêchant les autres clients d’établir de nouvelles connexions.
Les clients Blazor établissent une connexion unique par session et maintiennent la connexion ouverte tant que la fenêtre du navigateur est ouverte. Étant donné la nature persistante des connexions et la nature avec état des applications Blazor côté serveur, l’épuisement des connexions est un risque plus élevé pour la disponibilité de l’application.
Il n’existe aucune limite quant au nombre de connexions par utilisateur pour une application. Si l’application nécessite une limite de connexion, suivez une ou plusieurs des approches suivantes :
- Exigez l’authentification, ce qui limite naturellement la capacité des utilisateurs non autorisés à se connecter à l’application. Pour que ce scénario soit efficace, les utilisateurs doivent être empêchés de provisionner de nouveaux utilisateurs à la demande.
- Limitez le nombre de connexions par utilisateur. La limitation des connexions peut être effectuée à l’aide des approches suivantes. Veillez à autoriser les utilisateurs légitimes à accéder à l’application (par exemple, lorsqu’une limite de connexion est établie en fonction de l’adresse IP du client).
- Niveau application
- Extensibilité du routage du point de terminaison.
- Exigez l’authentification pour se connecter à l’application et effectuez le suivi des sessions actives par utilisateur.
- Rejetez les nouvelles sessions lorsque vous atteignez une limite.
- Les connexions WebSocket proxy à une application via l’utilisation d’un proxy, comme Azure SignalR Service, qui multiplexe les connexions des clients à une application. Cela fournit une application avec une capacité de connexion supérieure à celle qu’un seul client peut établir, ce qui empêche un client d’épuiser les connexions au serveur.
- Niveau serveur
- Utilisez un proxy ou une passerelle devant l’application. Par exemple, Azure Application Gateway est un équilibreur de charge de trafic web (couche OSI 7) qui vous permet de gérer le trafic vers vos applications web. Pour plus d’informations, consultez Vue d’ensemble de la prise en charge de WebSocket dans Application Gateway.
- Bien que l’interrogation longue soit prise en charge pour les applications Blazor, ce qui permettrait l’adoption d’Azure Front Door, WebSockets est le protocole de transport recommandé. Actuellement, en septembre 2024, Azure Front Door ne prend pas en charge les WebSockets, mais la prise en charge des WebSockets est en cours d’examen. Pour plus d’informations, consultez Prise en charge des connexions WebSocket sur Azure Front Door.
- Niveau application
- Exigez l’authentification, ce qui limite naturellement la capacité des utilisateurs non autorisés à se connecter à l’application. Pour que ce scénario soit efficace, les utilisateurs doivent être empêchés de provisionner de nouveaux utilisateurs à la demande.
- Limitez le nombre de connexions par utilisateur. La limitation des connexions peut être effectuée à l’aide des approches suivantes. Veillez à autoriser les utilisateurs légitimes à accéder à l’application (par exemple, lorsqu’une limite de connexion est établie en fonction de l’adresse IP du client).
- Niveau application
- Extensibilité du routage du point de terminaison.
- Exigez l’authentification pour se connecter à l’application et effectuez le suivi des sessions actives par utilisateur.
- Rejetez les nouvelles sessions lorsque vous atteignez une limite.
- Les connexions WebSocket proxy à une application via l’utilisation d’un proxy, comme Azure SignalR Service, qui multiplexe les connexions des clients à une application. Cela fournit une application avec une capacité de connexion supérieure à celle qu’un seul client peut établir, ce qui empêche un client d’épuiser les connexions au serveur.
- Niveau serveur
- Utilisez un proxy ou une passerelle devant l’application.
- Bien que l’interrogation longue soit prise en charge pour les applications Blazor, WebSockets est le protocole de transport recommandé. Nous vous recommandons de sélectionner un proxy ou une passerelle prenant en charge les WebSockets.
- Niveau application
Attaques par déni de service (DoS)
Les attaques par déni de service (DoS) impliquent un client qui entraîne l’épuisement d’une ou plusieurs des ressources du serveur, ce qui rend l’application indisponible. Les applications Blazor incluent des limites par défaut et s’appuient sur d’autres limites ASP.NET Core et SignalR définies sur CircuitOptions pour se protéger contre les attaques DoS :
- CircuitOptions.DisconnectedCircuitMaxRetained
- CircuitOptions.DisconnectedCircuitRetentionPeriod
- CircuitOptions.JSInteropDefaultCallTimeout
- CircuitOptions.MaxBufferedUnacknowledgedRenderBatches
- HubConnectionContextOptions.MaximumReceiveMessageSize
Pour plus d’informations et des exemples de codage de configuration, consultez les articles suivants :
Interactions avec le navigateur (client)
Un client interagit avec le serveur par le biais de la distribution d’événements d’interopérabilité JS et de l’achèvement du rendu. La communication d’interopérabilité JS va dans les deux sens entre JavaScript et .NET :
- Les événements de navigateur sont distribués du client au serveur de manière asynchrone.
- Le serveur répond de manière asynchrone en remettant l’interface utilisateur en fonction des besoins.
Fonctions JavaScript appelées à partir de .NET
Pour les appels de méthodes .NET à JavaScript :
- Tous les appels ont un délai d’expiration configurable après lequel ils échouent, renvoyant un OperationCanceledException à l’appelant.
- Il existe un délai d’attente par défaut pour les appels (CircuitOptions.JSInteropDefaultCallTimeout) d’une minute. Pour configurer cette limite, consultez Appeler des fonctions JavaScript à partir de méthodes .NET dans ASP.NET Core Blazor.
- Un jeton d’annulation peut être fourni pour contrôler l’annulation pour chaque appel. Comptez sur le délai d’expiration de la légende par défaut si possible, et liez un délai d’appel au client si un jeton d’annulation est fourni.
- Le résultat d’un appel JavaScript ne peut pas être approuvé. Le client d’application Blazor s’exécutant dans le navigateur recherche la fonction JavaScript à appeler. La fonction est appelée et le résultat ou une erreur est généré. Un client malveillant peut tenter de :
- Provoquer un problème dans l’application en retournant une erreur à partir de la fonction JavaScript.
- Induire un comportement inattendu sur le serveur en retournant un résultat inattendu à partir de la fonction JavaScript.
Prenez les précautions suivantes pour vous prémunir contre les scénarios précédents :
- Encapsulez les appels d’interopérabilité JS dans des instructions
try-catch
pour tenir compte des erreurs qui peuvent se produire pendant les appels. Pour plus d’informations, consultez Gérer les erreurs dans les applications ASP.NET Core Blazor. - Validez les données retournées par les appels d’interopérabilité JS, y compris les messages d’erreur, avant d’effectuer une action.
Méthodes .NET appelées à partir du navigateur
N’approuvez pas les appels de JavaScript vers des méthodes .NET. Lorsqu’une méthode .NET est exposée à JavaScript, tenez compte de la façon dont la méthode .NET est appelée :
- Traitez toute méthode .NET exposée à JavaScript comme un point de terminaison public pour l’application.
- Valider une entrée.
- Vérifiez que les valeurs se trouvent dans les plages attendues.
- Vérifiez que l’utilisateur a l’autorisation d’effectuer l’action demandée.
- N’allouez pas une quantité excessive de ressources dans le cadre de l’appel de la méthode .NET. Par exemple, effectuez des vérifications et placez des limites sur l’utilisation du processeur et de la mémoire.
- Prenez en compte le fait que les méthodes statiques et d’instance peuvent être exposées aux clients JavaScript. Évitez de partager l’état entre les sessions, sauf si la conception demande le partage d’état avec des contraintes appropriées.
- Pour les méthodes d’instance exposées via des objets DotNetObjectReference créés à l’origine par l’injection de dépendances (DI), les objets doivent être inscrits en tant qu’objets délimités. Cela s’applique à n’importe quel service de DI utilisé par l’application .
- Pour les méthodes statiques, évitez d’établir un état qui ne peut pas être étendu au client, sauf si l’application partage explicitement l’état par conception entre tous les utilisateurs d’une instance de serveur.
- Évitez de transmettre les données fournies par l’utilisateur dans les paramètres aux appels JavaScript. Si la transmission de données dans des paramètres est absolument nécessaire, assurez-vous que le code JavaScript gère la transmission des données sans introduire de vulnérabilités XSS (Cross-Site Scripting). Par exemple, n’écrivez pas de données fournies par l’utilisateur dans le modèle DOM (Document Object Model) en définissant la propriété
innerHTML
d’un élément. Envisagez d’utiliser une stratégie de sécurité du contenu (CSP) pour désactivereval
et d’autres primitives JavaScript non sécurisées. Pour plus d’informations, consultez Appliquer une stratégie de sécurité de contenu pour ASP.NET Core Blazor.
- Valider une entrée.
- Évitez d’implémenter la répartition personnalisée des appels .NET en plus de l’implémentation de la répartition du framework. L’exposition des méthodes .NET au navigateur est un scénario avancé, non recommandé pour le développement Blazor général.
Événements
Les événements fournissent un point d’entrée à une application. Les mêmes règles de protection des points de terminaison dans les applications web s’appliquent à la gestion des événements dans les applications Blazor. Un client malveillant peut envoyer toutes les données qu’il souhaite envoyer en tant que charge utile pour un événement.
Par exemple :
- Un événement de modification pour un
<select>
peut envoyer une valeur qui n’est pas dans les options présentées par l’application au client. - Un
<input>
peut envoyer des données texte au serveur, en contournant la validation côté client.
L’application doit valider les données pour tout événement géré par l’application. Les composants de formulaire du framework Blazor effectuent des validations de base. Si l’application utilise des composants de formulaire personnalisés, du code personnalisé doit être écrit pour valider les données d’événement comme nécessaire.
Les événements étant asynchrones, plusieurs événements peuvent être distribués au serveur avant que l’application n’ait le temps de réagir en produisant un nouveau rendu. Cela a certaines implications en matière de sécurité à prendre en compte. La limitation des actions du client dans l’application doit être effectuée à l’intérieur des gestionnaires d’événements et ne dépend pas de l’état d’affichage du rendu actuel.
Considérez un composant de compteur qui doit permettre à un utilisateur d’incrémenter un compteur au maximum trois fois. Le bouton permettant d’incrémenter le compteur est conditionnel et basé sur la valeur de count
:
<p>Count: @count</p>
@if (count < 3)
{
<button @onclick="IncrementCount" value="Increment count" />
}
@code
{
private int count = 0;
private void IncrementCount()
{
count++;
}
}
Un client peut distribuer un ou plusieurs événements d’incrément avant que le framework produise un nouveau rendu de ce composant. Le résultat est que le count
peut être incrémenté plus de trois fois par l’utilisateur, car le bouton n’est pas supprimé par l’interface utilisateur assez rapidement. La bonne façon d’atteindre la limite de trois incréments count
est illustrée dans l’exemple suivant :
<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++;
}
}
}
En ajoutant la vérification if (count < 3) { ... }
à l’intérieur du gestionnaire, la décision d’incrémenter count
est basée sur l’état actuel de l’application. La décision n’est pas basée sur l’état de l’interface utilisateur comme dans l’exemple précédent, cet état pouvant être temporairement obsolète.
Protection contre les envois multiples
Si un rappel d’événement appelle une opération de longue durée de manière asynchrone, comme l’extraction de données à partir d’un service ou d’une base de données externe, envisagez d’utiliser une protection. La protection peut empêcher l’utilisateur de mettre en file d’attente plusieurs opérations pendant que l’opération est en cours, avec des retours d’informations visuels. Le code de composant suivant définit isLoading
sur true
tandis que DataService.GetDataAsync
obtient des données à partir du serveur. Si isLoading
est true
, le bouton est désactivé dans l’interface utilisateur :
<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;
}
}
}
Le modèle de protection présenté dans l’exemple précédent fonctionne si l’opération en arrière-plan est exécutée de manière asynchrone avec le modèle async
-await
.
Annuler tôt et éviter l’utilisation après la suppression
En plus de l’utilisation d’une protection, comme décrit dans la section Protection contre les envois multiples, envisagez d’utiliser un CancellationToken pour annuler les opérations de longue durée lorsque le composant est supprimé. Cette approche présente l’avantage supplémentaire d’éviter l’utilisation après la suppression dans les composants :
@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();
}
}
Évitez les événements qui produisent de grandes quantités de données
Certains événements DOM, comme oninput
ou onscroll
, peuvent produire une grande quantité de données. Évitez d’utiliser ces événements dans le côté serveur du serveur Blazor.
Autres conseils de sécurité
Les conseils pour la sécurisation des applications ASP.NET Core s’appliquent aux applications Blazor côté serveur et sont abordés dans les sections suivantes de cet article :
- Journalisation et données sensibles
- Protéger les informations en transit avec HTTPS
- Scripting inter-site (XSS)
- Protection cross-origin
- Click-jacking
- Redirections ouvertes
Journalisation et données sensibles
Les interactions d’interopérabilité JS entre le client et le serveur sont enregistrées dans les journaux du serveur avec des instances ILogger. Blazor évite de journaliser les informations sensibles, comme les événements réels ou les entrées et sorties d’interopérabilité JS.
Lorsqu’une erreur se produit sur le serveur, le framework avertit le client et supprime la session. Le client reçoit un message d’erreur générique qui peut être consulté dans les outils de développement du navigateur.
L’erreur côté client n’inclut pas la pile des appels et ne fournit pas de détails sur la cause de l’erreur, mais les journaux du serveur contiennent de telles informations. À des fins de développement, des informations d’erreur sensibles peuvent être mises à la disposition du client en activant les erreurs détaillées.
Avertissement
L’exposition d’informations sur les erreurs aux clients sur Internet est un risque de sécurité qui doit toujours être évité.
Protéger les informations en transit avec HTTPS
Blazor utilise SignalR pour la communication entre le client et le serveur. Blazor utilise normalement le transport que SignalR négocie, qui est généralement WebSockets.
Blazor ne garantit pas l’intégrité et la confidentialité des données envoyées entre le serveur et le client. Toujours utiliser HTTPS.
Scripting inter-site (XSS)
Le scripting inter-site (XSS) permet à une partie non autorisée d’exécuter une logique arbitraire dans le contexte du navigateur. Une application compromise peut potentiellement exécuter du code arbitraire sur le client. La vulnérabilité peut être utilisée pour effectuer potentiellement un certain nombre d’actions malveillantes sur le serveur :
- Envoyer des événements faux/non valides au serveur.
- Envoyer des complétions de rendu fausses/non valides.
- Évitez de distribuer les complétions de rendu.
- Envoyer les appels d’interopérabilité de JavaScript à .NET.
- Modifier la réponse des appels d’interopérabilité de .NET à JavaScript.
- Évitez d’envoyer .NET aux résultats de l’interopérabilité JS.
Le framework Blazor prend des mesures pour se protéger contre certaines des menaces précédentes :
- Il cesse de produire de nouvelles mises à jour de l’interface utilisateur si le client n’accuse pas réception des lots de rendu. Configuré avec CircuitOptions.MaxBufferedUnacknowledgedRenderBatches.
- Il fait expirer tout appel .NET à JavaScript après une minute sans réception de réponse du client. Configuré avec CircuitOptions.JSInteropDefaultCallTimeout.
- Il effectue la validation de base sur toutes les entrées provenant du navigateur pendant l’interopérabilité JS :
- Les références .NET sont valides et du type attendu par la méthode .NET.
- Les données sont malformées.
- Le nombre correct d’arguments pour la méthode est présent dans la charge utile.
- Les arguments ou le résultat peuvent être désérialisés correctement avant d’appeler la méthode.
- Il effectue la validation de base dans toutes les entrées provenant du navigateur à partir d’événements distribués :
- L’événement a un type valide.
- Les données de l’événement peuvent être désérialisées.
- Un gestionnaire d’événements est associé à l’événement.
En plus des protections que le framework implémente, l’application doit être codée par le développeur pour se protéger contre les menaces et prendre les mesures appropriées :
- Validez toujours les données lors de la gestion des événements.
- Prenez les mesures appropriées lors de la réception de données non valides :
- Ignorez les données et retournez. Cela permet à l’application de continuer à traiter les requêtes.
- Si l’application détermine que l’entrée est illégitime et n’a pas pu être produite par un client légitime, levez une exception. La levée d’une exception détruit le circuit et met fin à la session.
- Ne faites pas confiance au message d’erreur fourni par les complétions de lot de rendu incluses dans les journaux. L’erreur est fournie par le client et n’est généralement pas de confiance, car le client peut être compromis.
- Ne faites pas confiance à l’entrée sur les appels d’interopérabilité JS dans l’une ou l’autre direction entre les méthodes JavaScript et .NET.
- L’application est chargée de vérifier que le contenu des arguments et des résultats est valide, même si les arguments ou les résultats sont correctement désérialisés.
Pour qu’une vulnérabilité XSS existe, l’application doit incorporer une entrée utilisateur dans la page rendue. Blazor exécute une étape de compilation où le balisage dans un fichier .razor
est transformé en logique C# procédurale. Au moment de l’exécution, la logique C# génère une arborescence de rendu décrivant les éléments, le texte et les composants enfants. Cela est appliqué au DOM du navigateur via une séquence d’instructions JavaScript (ou est sérialisé au format HTML dans le cas d’un prérendu) :
- L’entrée utilisateur rendue via une syntaxe Razor normale (par exemple,
@someStringValue
) n’expose pas de vulnérabilité XSS, car la syntaxe Razor est ajoutée au DOM via des commandes qui peuvent uniquement écrire du texte. Même si la valeur inclut du balisage HTML, la valeur est affichée sous forme de texte statique. Lors du prérendu, la sortie est encodée au format HTML, ce qui affiche également le contenu sous forme de texte statique. - Les balises de script ne sont pas autorisées et ne doivent pas être incluses dans l’arborescence de rendu des composants de l’application. Si une balise de script est incluse dans le balisage d’un composant, une erreur de compilation est générée.
- Les auteurs de composants peuvent créer des composants en C# sans utiliser Razor. L’auteur du composant est chargé d’utiliser les API appropriées lors de l’émission de la sortie. Par exemple, utilisez
builder.AddContent(0, someUserSuppliedString)
et nonbuilder.AddMarkupContent(0, someUserSuppliedString)
, car ce dernier risque de créer une vulnérabilité XSS.
Envisagez d’atténuer davantage les vulnérabilités XSS. Par exemple, implémentez une stratégie de sécurité du contenu (CSP) restrictive. Pour plus d’informations, consultez Appliquer une stratégie de sécurité de contenu pour ASP.NET Core Blazor.
Pour plus d’informations, consultez Empêcher le scripting inter-site (XSS) dans ASP.NET Core.
Protection cross-origin
Les attaques cross-origin impliquent qu’un client d’une autre origine effectue une action sur le serveur. L’action malveillante est généralement une requête GET ou un POST de formulaire (falsification de requête intersites, ou CSRF), mais l’ouverture d’un WebSocket malveillant est également possible. Les applications Blazor offrent les mêmes garanties que toutes les autres applications SignalR utilisant l’offre de protocole hub :
- L’accès aux applications peut se faire à partir d’origines différentes, sauf si des mesures supplémentaires sont prises pour empêcher cela. Pour désactiver l’accès Cross-Origin, désactivez CORS dans le point de terminaison en ajoutant l’intergiciel CORS au pipeline et en ajoutant DisableCorsAttribute aux métadonnées du point de terminaison Blazor, ou limitez l’ensemble d’origines autorisées par en configurant SignalR pour le partage de ressources Cross-Origin. Pour obtenir des conseils sur les restrictions d’origine WebSocket, consultez Prise en charge des WebSockets dans ASP.NET Core.
- Si CORS est activé, des étapes supplémentaires peuvent être nécessaires pour protéger l’application en fonction de la configuration de CORS. Si CORS est globalement activé, il peut être désactivé pour le hub BlazorSignalR en ajoutant les métadonnées DisableCorsAttribute aux métadonnées du point de terminaison après avoir appelé MapBlazorHub sur le générateur d’itinéraires de point de terminaison.
Pour plus d’informations, consultez Prévenir les attaques par falsification de requête intersites (XSRF/CSRF) dans ASP.NET Core.
Click-jacking
Le click-jacking implique le rendu d’un site en tant que <iframe>
à l’intérieur d’un site à partir d’une autre origine afin d’inciter l’utilisateur à effectuer des actions sur le site attaqué.
Pour protéger une application contre le rendu à l’intérieur d’un <iframe>
, utilisez une stratégie de sécurité du contenu (CSP) et l’en-tête X-Frame-Options
.
Pour plus d’informations, consultez les ressources suivantes :
- Appliquer une stratégie de sécurité du contenu pour ASP.NET Core Blazor
- Documents web MDN : X-Frame-Options
Redirections ouvertes
Lorsqu’une session d’application démarre, le serveur effectue la validation de base des URL envoyées dans le cadre du démarrage de la session. Le framework vérifie que l’URL de base est un parent de l’URL actuelle avant d’établir le circuit. Aucune vérification supplémentaire n’est effectuée par le framework.
Lorsqu’un utilisateur sélectionne un lien sur le client, l’URL du lien est envoyée au serveur, qui détermine l’action à entreprendre. Par exemple, l’application peut effectuer une navigation côté client ou indiquer au navigateur d’accéder au nouvel emplacement.
Les composants peuvent également déclencher des requêtes de navigation par programmation via l’utilisation de NavigationManager. Dans ces scénarios, l’application peut effectuer une navigation côté client ou indiquer au navigateur d’accéder au nouvel emplacement.
Les composants doivent :
- Éviter d’utiliser l’entrée utilisateur dans le cadre des arguments d’appel de navigation.
- Valider les arguments pour s’assurer que la cible est autorisée par l’application.
Sinon, un utilisateur malveillant pourrait forcer le navigateur à accéder à un site contrôlé par un cyberattaquant. Dans ce scénario, le cyberattaquant manipule l’application pour qu’elle utilise une entrée d’utilisateur comme élément d’appel de la méthode NavigationManager.NavigateTo.
Ce conseil s’applique également lors du rendu des liens dans le cadre de l’application :
- Si possible, utilisez des liens relatifs.
- Vérifiez que les destinations des liens absolus sont valides avant de les inclure dans une page.
Pour plus d’informations, consultez Empêcher les attaques par redirection ouverte dans ASP.NET Core.
Liste de contrôle de sécurité
La liste suivante de considérations relatives à la sécurité n’est pas exhaustive :
- Validez les arguments des événements.
- Validez les entrées et les résultats des appels d’interopérabilité JS.
- Évitez d’utiliser (ou validez au préalable) l’entrée utilisateur pour les pour les appels d’interopérabilité .NET à JS.
- Empêchez le client d’allouer une quantité de mémoire non limitée.
- Données dans le composant.
- Objets DotNetObjectReference retournés au client.
- Protection contre les envois multiples.
- Annulez les opérations de longue durée lorsque le composant est supprimé.
- Évitez les événements qui produisent de grandes quantités de données.
- Évitez d’utiliser l’entrée utilisateur dans le cadre des appels à NavigationManager.NavigateTo et validez l’entrée utilisateur pour les URL par rapport à un ensemble d’origines autorisées d’abord, si cela est inévitable.
- Ne prenez pas de décisions d’autorisation basées sur l’état de l’interface utilisateur, mais uniquement en fonction de l’état du composant.
- Envisagez d’utiliser une stratégie de sécurité du contenu (CSP) pour vous protéger contre les attaques XSS. Pour plus d’informations, consultez Appliquer une stratégie de sécurité de contenu pour ASP.NET Core Blazor.
- Envisagez d’utiliser une CSP et X-Frame-Options pour vous protéger contre le click-jacking.
- Vérifiez que les paramètres CORS sont appropriés lors de l’activation de CORS, ou désactivez explicitement CORS pour les applications Blazor.
- Testez pour vous assurer que les limites côté serveur de l’application Blazor offrent une expérience utilisateur acceptable sans niveaux de risque inacceptables.