Guía de mitigación de amenazas para la representación interactiva del lado servidor de ASP.NET Core Blazor
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
En este artículo se explica cómo mitigar las amenazas de seguridad en el lado servidor interactivo Blazor.
Las aplicaciones adoptan un modelo de procesamiento de datos con estado, donde el servidor y el cliente mantienen una relación de larga duración. El estado persistente se mantiene mediante un circuito, el cual puede abarcar conexiones que también son potencialmente de larga duración.
Cuando un usuario visita un sitio web de , el servidor crea un circuito en su memoria. Este circuito indica al explorador qué contenido se va a representar y responde a los eventos, como cuando el usuario selecciona un botón en la UI. Para realizar estas acciones, el circuito invoca funciones de JavaScript en el explorador del usuario y métodos de .NET en el servidor. Esta interacción bidireccional basada en JavaScript se conoce como interoperabilidad de JavaScript (interoperabilidad de JS).
Como la interoperabilidad de JS se produce por y el cliente usa un explorador remoto, las aplicaciones comparten la mayoría de los problemas de seguridad de las aplicaciones web. En este tema se describen las amenazas más habituales para las aplicaciones Blazor del lado servidor y se proporciona una guía de mitigación de amenazas centrada en las aplicaciones accesibles desde Internet.
En entornos restringidos, como redes o intranets corporativas, algunas de las instrucciones de mitigación:
- No se aplican en el entorno restringido.
- No merecen la pena porque el riesgo de seguridad en los entornos restringidos es bajo.
Componentes de servidor interactivo con compresión de WebSocket habilitada
La compresión puede exponer la aplicación a ataques de canal lateral contra el cifrado TLS de la conexión, como los ataques CRIME y BREACH. Estos tipos de ataques requieren que el ciberdelincuente:
- Forzar a un explorador a emitir solicitudes con una carga que el ciberdelincuente controla en un sitio vulnerable a través de la publicación de formularios entre sitios o insertando el sitio dentro de un iframe de otro sitio.
- Observe la longitud de la respuesta comprimida y cifrada a través de la red.
Para que la aplicación sea vulnerable, debe reflejar la carga del ciberdelincuente en la respuesta, por ejemplo, escribiendo la ruta de acceso o la cadena de consulta en la respuesta. Con la longitud de la respuesta, el ciberdelincuentes puede "adivinar" cualquier información sobre la respuesta, pasando el cifrado de la conexión.
Por lo general, las Blazor aplicaciones pueden habilitar la compresión a través de la conexión de WebSocket con las medidas de seguridad adecuadas:
La aplicación puede ser vulnerable cuando toma contenido de la solicitud (por ejemplo, la ruta de acceso o la cadena de consulta) que un ciberdelincuente puede influir en él y lo reproduce en el HTML de la página o lo convierte en parte de la respuesta.
Blazor aplica automáticamente las siguientes medidas de seguridad:
Cuando se configura la compresión, Blazor bloquea automáticamente la inserción de la aplicación en un iframe, que bloquea la respuesta inicial (sin comprimir) del servidor de la representación e impide que la conexión WebSocket se inicie nunca.
La restricción para insertar la aplicación en un iframe se puede relajar. Sin embargo, la relajación de la restricción expone la aplicación para atacar si el documento de inserción se pone en peligro a través de una vulnerabilidad de scripting entre sitios, ya que proporciona al ciberdelincuentes una manera de ejecutar el ataque.
Normalmente, para que este tipo de ataque se produzca, la aplicación debe reproducir repetidamente el contenido en las respuestas para que el ciberdelincuente pueda adivinar la respuesta. Dado cómo Blazor representa (se representa una vez y, a continuación, genera diferencias del contenido solo para los elementos que han cambiado), esto es difícil para que un ciberdelincuente lo logre. Sin embargo, no es imposible para un ciberdelincuente, por lo que se debe tener cuidado para evitar la representación de información confidencial junto con la información externa que un ciberdelincuente puede manipular. A continuación se indican algunos ejemplos:
Representar información de identificación personal (PII) en la página al mismo tiempo que representar datos de base de datos agregados por otro usuario.
Representar información de PII en la página al mismo tiempo que los datos procedentes de otro usuario a través de JS interoperabilidad o un servicio singleton local en el servidor.
En general, se recomienda evitar la representación de componentes que contienen información confidencial junto con componentes que pueden representar datos de orígenes que no son de confianza como parte del mismo lote de representación. Los orígenes que no son de confianza incluyen parámetros de ruta, cadenas de consulta, datos de interoperabilidad JS y cualquier otro origen de datos que un usuario de terceros pueda controlar (bases de datos, servicios externos).
Estado compartido
Las aplicaciones Blazor del lado servidor residen en la memoria del servidor, y varias sesiones de aplicación se hospedan en el mismo proceso. Para cada sesión de aplicación, Blazor inicia un circuito con su propio ámbito de contenedor de inserción de dependencias, por lo que los servicios con ámbito son únicos para cada sesión de Blazor.
Advertencia
No se recomienda que las aplicaciones del mismo servidor compartan estado mediante servicios singleton, a menos que se tomen precauciones, ya que esto puede incorporar vulnerabilidades de seguridad, como la pérdida de estado de usuario entre circuitos.
Puede usar servicios singleton con estado en aplicaciones de Blazor si están específicamente diseñadas para ello. Por ejemplo, el uso de una caché de memoria singleton es aceptable porque una memoria caché requiere una clave para acceder a una entrada determinada. Suponiendo que los usuarios no tengan control sobre las claves de caché que se usan con la memoria caché, el estado almacenado en la memoria caché no se filtra entre circuitos.
Para obtener instrucciones generales sobre la administración de estados, consulte: Administración de estados de Blazor en ASP.NET Core.
IHttpContextAccessor
/HttpContext
en Razor componentes
IHttpContextAccessor debe evitarse con la representación interactiva porque no hay un HttpContext
válido disponible.
IHttpContextAccessor se puede usar para los componentes que se representan estáticamente en el servidor. Sin embargo, se recomienda evitarlo si es posible.
HttpContextse puede usar como parámetro en cascada solo en componentes raíz representados estáticamente para tareas generales, como inspeccionar y modificar encabezados u otras propiedades del componente App
(Components/App.razor
). El valor siempre es null
para la representación interactiva.
[CascadingParameter]
public HttpContext? HttpContext { get; set; }
En escenarios en los que se requiere el HttpContext en componentes interactivos, se recomienda hacer fluir los datos a través del estado del componente persistente desde el servidor. Para más información, veaOtros escenarios de seguridad de Blazor en ASP.NET Core del lado servidor.
No use IHttpContextAccessor/HttpContext directa o indirectamente en los componentes Razor de las aplicaciones Blazor del lado servidor. Las aplicaciones de Blazor se ejecutan fuera del contexto de la canalización de ASP.NET Core. No se garantiza que HttpContext esté disponible en IHttpContextAccessor, ni tampoco que HttpContext contenga el contexto que ha iniciado la aplicación de Blazor.
El enfoque recomendado para pasar el estado de solicitud a la aplicación de Blazor es a través de parámetros de componente raíz durante la representación inicial de la aplicación. Como alternativa, la aplicación puede copiar los datos en un servicio con ámbito en el evento de ciclo de vida de inicialización del componente raíz para usarlos en toda la aplicación. Para más información, veaOtros escenarios de seguridad de Blazor en ASP.NET Core del lado servidor.
Un aspecto crítico de la seguridad de Blazor en el lado del servidor es que el usuario asociado a un circuito determinado se puede actualizar en algún momento después de que se establezca el circuito de Blazor, pero IHttpContextAccessor no se actualice. Para más información sobre cómo abordar esta situación con servicios personalizados, vea Otros escenarios de seguridad de Blazor en ASP.NET Core del lado servidor.
Agotamiento de recursos
El agotamiento de recursos puede producirse cuando un cliente interactúa con el servidor y hace que este consuma demasiados recursos. El consumo excesivo de recursos afecta principalmente a los siguientes elementos:
Los ataques por denegación de servicio (DoS) suelen intentar agotar los recursos de una aplicación o de un servidor. Pero el agotamiento de recursos no tiene por qué deberse a un ataque en el sistema. Por ejemplo, una cantidad limitada de recursos se puede agotar debido a una alta demanda de los usuarios. DoS se trata más adelante en la sección DoS.
Los recursos externos al marco de Blazor, como las bases de datos y los identificadores de archivo (que se usan para leer y escribir archivos), también pueden experimentar el agotamiento de recursos. Para obtener más información, consulte Procedimientos recomendados de ASP.NET Core.
CPU
El agotamiento de la CPU puede producirse cuando uno o varios clientes fuerzan al servidor a realizar trabajos que consumen mucha CPU.
Por ejemplo, imagine una aplicación que calcula un número de Fibonacci. Un número de Fibonacci se genera a partir de una secuencia de Fibonacci, en la que cada número es la suma de los dos anteriores. La cantidad de trabajo necesaria para obtener una respuesta depende de la longitud de la secuencia y del tamaño del valor inicial. Si la aplicación no establece límites en la solicitud de un cliente, los cálculos que requieren un uso intensivo de la CPU pueden acaparar el tiempo de la CPU y reducir el rendimiento de otras tareas. Un consumo excesivo de recursos es un problema de seguridad que afecta a la disponibilidad.
El agotamiento de la CPU supone un problema para todas las aplicaciones de acceso público. En las aplicaciones web normales, las solicitudes y las conexiones agotan el tiempo de espera como medida de seguridad, pero las aplicaciones Blazor no proporcionan las mismas medidas de seguridad. Las aplicaciones Blazor deben incluir las comprobaciones y los límites pertinentes para poder llevar a cabo un trabajo que requiera un uso potencialmente intensivo de la CPU.
Memoria
El agotamiento de la memoria puede producirse cuando uno o varios clientes fuerzan al servidor a consumir una gran cantidad de memoria.
Por ejemplo, imagine una aplicación con un componente que acepta y muestra una lista de elementos. Si la aplicación Blazor no impone límites en el número de elementos permitidos o en el número de elementos representados en el cliente, el procesamiento y la representación que necesitan mucha memoria pueden dominar la memoria del servidor hasta el punto en el que el rendimiento del servidor puede verse afectado. El servidor puede bloquearse o ralentizarse hasta el punto de parecer que se ha bloqueado.
Ten en cuenta lo siguiente a la hora de mantener y mostrar una lista de los elementos que pertenecen a un posible escenario de agotamiento de la memoria en el servidor:
- Los elementos de una propiedad o de un campo
List<T>
usan la memoria del servidor. Si la aplicación permite que la lista de elementos crezca sin límites, existe el riesgo de que el servidor se quede sin memoria. Al quedarse sin memoria, la sesión actual finaliza (se bloquea) y todas las sesiones simultáneas en esa instancia de servidor reciben una excepción de memoria insuficiente. Para evitar que se produzca este escenario, la aplicación debe usar una estructura de datos que imponga un límite de elementos a los usuarios simultáneos. - Si no se usa un esquema de paginación para la representación, el servidor consume memoria adicional para los objetos que no están visibles en la interfaz de usuario. Sin un límite en cuanto al número de elementos, las demandas de memoria pueden agotar la memoria disponible del servidor. Para impedir este escenario, sigue uno de estos procedimientos:
- Usa listas paginadas para la representación.
- Muestra solo los primeros elementos (entre 100 y 1000) y exige al usuario que escriba criterios de búsqueda para ver más elementos de los mostrados.
- Para un escenario de representación más avanzado, implementa listas o cuadrículas que admitan la virtualización. Con la virtualización, las listas solo representan un subconjunto de elementos actualmente visibles para el usuario. Cuando el usuario interactúa con la barra de desplazamiento en la UI, el componente solo representa los elementos que es necesario mostrar. Los elementos que no es necesario mostrar en ese momento se pueden conservar en el almacenamiento secundario; este es el enfoque ideal. Los elementos no mostrados también se pueden almacenar en la memoria, lo que resulta menos idóneo.
Nota
Blazor tiene compatibilidad integrada con virtualización. Para obtener más información, consulta Virtualización de componentes Razor de ASP.NET Core.
Las aplicaciones Blazor ofrecen un modelo de programación similar a otros marcos de UI para las aplicaciones con estado, como WPF, Windows Forms o Blazor WebAssembly. La principal diferencia es que, en varios marcos de interfaz de usuario, la memoria consumida por la aplicación pertenece al cliente y solo afecta a ese cliente en concreto. Por ejemplo, una aplicación Blazor WebAssembly se ejecuta completamente en el cliente y solo usa recursos de la memoria del cliente. Para una aplicación Blazor del lado servidor, la memoria que consume pertenece al servidor y se comparte entre los clientes en la instancia del servidor.
Las demandas de memoria del lado servidor se deben tener en cuenta para todas las aplicaciones Blazor del lado servidor. Sin embargo, la mayoría de las aplicaciones web no tienen estado y la memoria que se usa al procesar una solicitud se libera en cuanto se devuelve una respuesta. Como recomendación general, no permitas que los clientes asignen una cantidad de memoria sin límite, como en cualquier otra aplicación del lado servidor que conserve las conexiones de cliente. La memoria que consume una aplicación Blazor del lado servidor persiste durante más tiempo que una única solicitud.
Nota:
Durante el desarrollo, se puede usar un generador de perfiles o un seguimiento capturado para evaluar las demandas de memoria de los clientes. Un generador de perfiles o un seguimiento no capturan la memoria asignada a un cliente en concreto. Para capturar el uso de memoria de un cliente específico durante el desarrollo, captura un volcado de memoria y examina la demanda de memoria de todos los objetos cuya raíz se encuentre en el circuito de ese usuario.
Conexiones de cliente
Puede producirse un agotamiento de las conexiones si uno o varios clientes abren demasiadas conexiones simultáneas al servidor, lo que impide que otros clientes establezcan nuevas conexiones.
Los clientes de Blazor establecen una conexión única por sesión y la mantienen abierta mientras la ventana del explorador está abierta. Dada la naturaleza persistente de las conexiones y la naturaleza con estado de las aplicaciones Blazor del lado servidor, el agotamiento de las conexiones supone un riesgo mayor para la disponibilidad de la aplicación.
No hay ningún límite en cuanto al número de conexiones por usuario para una aplicación. Si la aplicación requiere un límite de conexiones, sigue uno de estos procedimientos:
- Requiere autenticación para limitar de forma natural la posibilidad de que usuarios no autorizados se conecten a la aplicación. Para que este escenario sea eficaz, se debe impedir que los usuarios puedan aprovisionar nuevos usuarios a petición.
- Limita el número de conexiones por usuario. La limitación de las conexiones se puede realizar de las siguientes maneras. Presta atención para permitir que los usuarios legítimos tengan acceso a la aplicación (por ejemplo, cuando se establece un límite de conexiones en función de la dirección IP del cliente).
- Nivel de aplicación
- Implementa la extensibilidad de enrutamiento de puntos de conexión.
- Requiere autenticación para conectarse a la aplicación y realiza un seguimiento de las sesiones activas por usuario.
- Rechaza nuevas sesiones cuando se alcance un límite.
- Redirige las conexiones WebSocket a una aplicación mediante un proxy, como Azure SignalR Service, que multiplexa las conexiones de los clientes a una aplicación. Esto proporciona una aplicación con mayor capacidad de conexión de la que puede establecer un solo cliente, lo que impide que un único cliente agote las conexiones al servidor.
- Nivel de servidor
- Usa un proxy o una puerta de enlace delante de la aplicación. Por ejemplo, Azure Application Gateway es un equilibrador de carga de tráfico web (OSI capa 7) que permite administrar el tráfico a las aplicaciones web. Para obtener más información, consulta Introducción a la compatibilidad de WebSocket en Application Gateway.
- Aunque el sondeo largo es compatible con las aplicaciones Blazor, lo que permitiría la adopción de Azure Front Door, WebSockets es el protocolo de transporte recomendado. A partir de septiembre de 2024, Azure Front Door no es compatible con WebSockets, pero se está considerando la compatibilidad con WebSockets. Para obtener más información, consulta Compatibilidad con conexiones WebSocket en Azure Front Door.
- Nivel de aplicación
- Requiere autenticación para limitar de forma natural la posibilidad de que usuarios no autorizados se conecten a la aplicación. Para que este escenario sea eficaz, se debe impedir que los usuarios puedan aprovisionar nuevos usuarios a petición.
- Limita el número de conexiones por usuario. La limitación de las conexiones se puede realizar de las siguientes maneras. Presta atención para permitir que los usuarios legítimos tengan acceso a la aplicación (por ejemplo, cuando se establece un límite de conexiones en función de la dirección IP del cliente).
- Nivel de aplicación
- Implementa la extensibilidad de enrutamiento de puntos de conexión.
- Requiere autenticación para conectarse a la aplicación y realiza un seguimiento de las sesiones activas por usuario.
- Rechaza nuevas sesiones cuando se alcance un límite.
- Redirige las conexiones WebSocket a una aplicación mediante un proxy, como Azure SignalR Service, que multiplexa las conexiones de los clientes a una aplicación. Esto proporciona una aplicación con mayor capacidad de conexión de la que puede establecer un solo cliente, lo que impide que un único cliente agote las conexiones al servidor.
- Nivel de servidor
- Usa un proxy o una puerta de enlace delante de la aplicación.
- Aunque el sondeo largo es compatible con las aplicaciones de Blazor, WebSockets es el protocolo de transporte recomendado. Se recomienda seleccionar un proxy o puerta de enlace que admita WebSockets.
- Nivel de aplicación
Ataques por denegación de servicio (DoS)
Los ataques por denegación de servicio (DoS) se producen cuando un cliente provoca que el servidor agote uno o más de sus recursos, lo que hace que la aplicación no esté disponible. Las aplicaciones Blazor incluyen límites predeterminados y dependen de otros límites de ASP.NET Core y SignalR que se establecen en CircuitOptions para ofrecer protección contra los ataques DoS:
- CircuitOptions.DisconnectedCircuitMaxRetained
- CircuitOptions.DisconnectedCircuitRetentionPeriod
- CircuitOptions.JSInteropDefaultCallTimeout
- CircuitOptions.MaxBufferedUnacknowledgedRenderBatches
- HubConnectionContextOptions.MaximumReceiveMessageSize
Para obtener más información y ejemplos de codificación de configuración, consulta los artículos siguientes:
Interacciones con el explorador (cliente)
Un cliente interactúa con el servidor a través del envío de eventos de interoperabilidad de JS y la finalización de la representación. La comunicación de interoperabilidad de JS es bidireccional entre JavaScript y .NET:
- Los eventos del explorador se envían desde el cliente al servidor de manera asincrónica.
- El servidor responde de forma asincrónica mediante la representación de la interfaz de usuario según sea necesario.
Funciones de JavaScript invocadas desde .NET
En las llamadas a funciones de JavaScript desde métodos de .NET:
- Todas las invocaciones tienen un tiempo de espera configurable después del cual se produce un error y se devuelve una excepción OperationCanceledException al autor de la llamada.
- Hay un tiempo de espera predeterminado de un minuto para las llamadas (CircuitOptions.JSInteropDefaultCallTimeout). Para configurar este límite, consulta Llamada a funciones de JavaScript desde métodos de .NET en Blazor de ASP.NET Core.
- Se puede proporcionar un token de cancelación para controlar la cancelación en cada llamada. Confía en el tiempo de espera predeterminado de cada llamada, siempre que sea posible, y limita el tiempo de una llamada al cliente si se proporciona un token de cancelación.
- No se puede confiar en el resultado de una llamada de JavaScript. El cliente de la aplicación Blazor que se está ejecutando en el explorador busca la función de JavaScript que se va a invocar. Se invoca la función y se obtiene el resultado o se produce un error. Un cliente malintencionado podría intentar lo siguiente:
- Provocar un problema en la aplicación devolviendo un error de la función de JavaScript.
- Inducir un comportamiento no deseado en el servidor devolviendo un resultado inesperado de la función de JavaScript.
Toma las siguientes precauciones para protegerte frente a los escenarios anteriores:
- Encapsula las llamadas de interoperabilidad de JS dentro de instrucciones
try-catch
para tener en cuenta los errores que pueden producirse durante las invocaciones. Para obtener más información, consulta Control de errores en aplicaciones Blazor de ASP.NET Core. - Valida los datos devueltos por las invocaciones de interoperabilidad de JS, incluidos los mensajes de error, antes de realizar cualquier acción.
Métodos de .NET invocados desde el explorador
No confíes en las llamadas de JavaScript a métodos de .NET. Cuando un método de .NET se exponga a JavaScript, presta atención a cómo se invoca al método de .NET:
- Trate cualquier método de .NET expuesto a JavaScript como haría con un punto de conexión público a la aplicación.
- Valide la entrada.
- Asegúrate de que los valores se encuentran dentro de los intervalos esperados.
- Asegúrate de que el usuario tiene permiso para realizar la acción solicitada.
- No asignes una cantidad excesiva de recursos como parte de la invocación al método de .NET. Por ejemplo, realiza comprobaciones y establece límites en el uso de la CPU y la memoria.
- Ten en cuenta que los métodos estáticos y de instancia se pueden exponer a clientes de JavaScript. Evita compartir el estado entre las sesiones a menos que el diseño requiera su uso compartido con las restricciones pertinentes.
- Para los métodos de instancia expuestos a través de objetos DotNetObjectReference que se crearon originalmente mediante la inserción de dependencias, los objetos se deben registrar como objetos de ámbito. Esto se aplica a cualquier servicio de inserción de dependencias que use la aplicación .
- En el caso de los métodos estáticos, evita establecer un estado que no se pueda limitar al cliente a menos que la aplicación comparta explícitamente el estado por diseño entre todos los usuarios de una instancia de servidor.
- Evita pasar datos proporcionados por el usuario en parámetros a llamadas de JavaScript. Si es absolutamente necesario pasar los datos en parámetros, asegúrate de que el código de JavaScript controla el paso de datos sin introducir vulnerabilidades frente a ataques de scripts entre sitios (XSS). Por ejemplo, no escribas datos proporcionados por el usuario en Document Object Model (DOM) estableciendo la propiedad
innerHTML
de un elemento. Considera la posibilidad de usar la Directiva de seguridad de contenido (CSP) para deshabilitareval
y otros elementos primitivos de JavaScript no seguros. Para obtener más información, consulta Aplicación de una directiva de seguridad de contenido para Blazor de ASP.NET Core.
- Valide la entrada.
- Evita implementar la distribución personalizada de invocaciones de .NET sobre la implementación de distribución del marco. La exposición de métodos de .NET en el explorador es un escenario avanzado, no se recomienda para el desarrollo general de Blazor.
Eventos
Los eventos proporcionan un punto de entrada a una aplicación. Las mismas reglas que se usan para proteger los puntos de conexión de las aplicaciones web se aplican al control de eventos en las aplicaciones Blazor. Un cliente malintencionado puede enviar los datos que quiera como la carga de un evento.
Por ejemplo:
- Un evento de cambio para un elemento
<select>
podría enviar un valor que no está dentro de las opciones que la aplicación presenta al cliente. - Un elemento
<input>
podría enviar datos de texto al servidor omitiendo la validación del lado cliente.
La aplicación debe validar los datos de los eventos que controla. En el marco Blazor, los componentes de formularios llevan a cabo validaciones básicas. Si la aplicación usa componentes de formularios personalizados, se debe escribir código personalizado para validar los datos de los eventos según corresponda.
Los eventos son asincrónicos, por lo que se pueden enviar varios eventos al servidor antes de que la aplicación tenga tiempo de reaccionar mediante la generación de una nueva representación. Esto tiene algunas implicaciones de seguridad que se deben tener en cuenta. La limitación de las acciones de los clientes en la aplicación debe realizarse dentro de los controladores de eventos y no debe depender del estado de visualización representado en ese momento.
Imagínate que tienes un componente de contador que debe permitir que un usuario incremente un contador un máximo de tres veces. El botón para incrementar el contador se basa condicionalmente en el valor 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 cliente puede enviar uno o varios eventos de incremento antes de que el marco genere una nueva representación de este componente. El resultado es que el usuario puede incrementar count
más de tres veces porque la interfaz de usuario no elimina el botón lo bastante rápido. En el ejemplo siguiente se muestra la manera correcta de limitar a tres los incrementos de count
:
<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++;
}
}
}
Al agregar la comprobación if (count < 3) { ... }
en el controlador, la decisión de incrementar count
se basa en el estado actual de la aplicación. La decisión no se basa en el estado de la interfaz de usuario, como era el caso en el ejemplo anterior, por lo que el estado podría estar temporalmente obsoleto.
Protección contra varios envíos
Si una devolución de llamada de evento invoca de forma asincrónica una operación de larga duración, como la recuperación de datos de una base de datos o de un servicio externos, considere la posibilidad de usar una salvaguarda. La salvaguarda puede impedir que el usuario ponga en cola varias operaciones mientras la operación está en curso mediante comentarios visuales. El siguiente código de componente establece isLoading
en true
mientras DataService.GetDataAsync
obtiene los datos del servidor. Mientras isLoading
es true
, el botón está deshabilitado en la UI:
<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;
}
}
}
El patrón de salvaguarda que se muestra en el ejemplo anterior solo funciona si la operación en segundo plano se ejecuta de forma asincrónica con el patrón async
-await
Cancelación anticipada para evitar el uso tras la eliminación del componente
Además de usar una salvaguarda como se describe en la sección Protección contra varios envíos, considera la posibilidad de usar un CancellationToken para cancelar las operaciones de ejecución prolongada cuando se elimine el componente. Este enfoque tiene la ventaja adicional de evitar el uso tras la eliminación de los componentes:
@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();
}
}
Evitación de eventos que generan grandes cantidades de datos
Algunos eventos DOM, como oninput
o onscroll
, pueden generar una gran cantidad de datos. Evita el uso de estos eventos en las aplicaciones Blazor del lado servidor.
Instrucciones de seguridad adicionales
Las instrucciones para proteger las aplicaciones de ASP.NET Core se aplican también a las aplicaciones Blazor del lado servidor y se describen en las siguientes secciones de este artículo:
- Registro y datos confidenciales
- Protección de la información en tránsito con HTTPS
- Scripts entre sitios (XSS)
- Protección entre orígenes
- Secuestro de clic
- Redireccionamientos abiertos
Registro y datos confidenciales
Las interacciones de interoperabilidad de JS entre el cliente y el servidor se registran en los registros del servidor con instancias de ILogger. Blazor evita el registro de información confidencial, como eventos reales o entradas y salidas de interoperabilidad de JS.
Cuando se produce un error en el servidor, el marco notifica al cliente y anula la sesión. El cliente recibe un mensaje de error genérico que se puede consultar en las herramientas de desarrollo del explorador.
El error del lado cliente no incluye la pila de llamadas y no proporciona detalles sobre la causa del error, pero los registros del servidor sí contienen dicha información. La información de errores confidencial puede ponerse a disposición del cliente con fines de desarrollo mediante la habilitación de errores detallados.
Advertencia
La exposición de información de errores a los clientes en Internet es un riesgo de seguridad que debe evitarse siempre.
Protección de la información en tránsito con HTTPS
Blazor usa SignalR para la comunicación entre el cliente y el servidor. Normalmente, Blazor usa el transporte que negocia SignalR, que suele ser WebSockets.
Blazor no garantiza la integridad y confidencialidad de los datos enviados entre el servidor y el cliente. Usa siempre HTTPS.
Scripts entre sitios (XSS)
Los ataques de scripts entre sitios (XSS) permiten que una entidad no autorizada ejecute una lógica arbitraria en el contexto del explorador. Una aplicación en peligro podría ejecutar código arbitrario en el cliente. Esta vulnerabilidad podría usarse para realizar una serie de acciones malintencionadas en el servidor:
- Enviar eventos falsos o no válidos al servidor.
- Enviar finalizaciones de representaciones no válidas o con errores.
- Evitar el envío de finalizaciones de representaciones.
- Enviar llamadas de interoperabilidad de JavaScript a .NET.
- Modificar la respuesta de las llamadas de interoperabilidad de .NET a JavaScript.
- Evitar el envío de .NET a los resultados de interoperabilidad de JS.
El marco de Blazor toma las medidas necesarias para protegerle frente a algunas de las amenazas anteriores:
- Detiene la generación de nuevas actualizaciones de la interfaz de usuario si el cliente no reconoce los lotes de representación. Se configura con CircuitOptions.MaxBufferedUnacknowledgedRenderBatches.
- Agota el tiempo de espera de cualquier llamada de .NET a JavaScript pasado un minuto sin recibir una respuesta del cliente. Se configura con CircuitOptions.JSInteropDefaultCallTimeout.
- Realiza la validación básica de todas las entradas procedentes del explorador durante la interoperabilidad de JS:
- Las referencias de .NET son válidas y del tipo esperado por el método de .NET.
- Los datos no tienen un formato incorrecto.
- La carga cuenta con el número adecuado de argumentos para el método.
- Los argumentos o el resultado se pueden deserializar correctamente antes de invocar el método.
- Realiza la validación básica en todas las entradas procedentes del explorador a partir de los eventos enviados:
- El evento tiene un tipo válido.
- Los datos para el evento se pueden deserializar.
- Hay un controlador de eventos asociado al evento.
Además de las medidas de seguridad que implementa el marco, el desarrollador debe codificar la aplicación para protegerla frente a amenazas y debe realizar las siguientes acciones:
- Validar siempre los datos al controlar los eventos.
- Tomar las medidas adecuadas al recibir datos no válidos:
- Omitir los datos y devolverlos. Esto permite que la aplicación siga procesando las solicitudes.
- Iniciar una excepción si la aplicación determina que la entrada es ilegítima y no la ha podido generar un cliente legítimo. Al iniciar una excepción, se anula el circuito y finaliza la sesión.
- No confiar en el mensaje de error proporcionado por las finalizaciones de lotes de representación incluidas en los registros. El cliente proporciona el error y, por lo general, no se puede confiar en él, ya que es posible que esté en peligro.
- No confiar en la entrada de las llamadas de interoperabilidad de JS entre los métodos de .NET y JavaScript y viceversa.
- La aplicación se encarga de validar que el contenido de los argumentos y de los resultados es válido, incluso si los argumentos o los resultados están correctamente deserializados.
Para que exista una vulnerabilidad frente a ataques de scripts entre sitios (XSS), la aplicación debe incorporar los datos proporcionados por el usuario en la página representada. Blazor ejecuta un paso en tiempo de compilación en el que el marcado de un archivo .razor
se transforma en una lógica de procedimientos de C#. En tiempo de ejecución, la lógica de C# crea un árbol de representación que describe los elementos, el texto y los componentes secundarios. Esto se aplica al DOM del explorador a través de una secuencia de instrucciones de JavaScript (o se serializa a HTML en el caso de realizar una representación previa):
- Los datos proporcionados por el usuario mediante la sintaxis normal de Razor (por ejemplo,
@someStringValue
) no exponen una vulnerabilidad frente a ataques de scripts entre sitios (XSS) porque la sintaxis de Razor se agrega al DOM a través de comandos que solo pueden escribir texto. Aunque el valor incluya marcado HTML, se muestra como texto estático. Al generar una representación previa, la salida está codificada en HTML, que también muestra el contenido como texto estático. - No se permiten etiquetas de script y no se deben incluir en el árbol de representación de componentes de la aplicación. Si se incluye una etiqueta de script en el marcado de un componente, se genera un error en tiempo de compilación.
- Los autores de componentes pueden crear componentes en C# sin usar Razor. El autor del componente es responsable de usar las API correctas al emitir la salida. Por ejemplo, debe usar
builder.AddContent(0, someUserSuppliedString)
y nobuilder.AddMarkupContent(0, someUserSuppliedString)
, ya que esta última podría crear una vulnerabilidad frente a ataques de scripts entre sitios (XSS).
Considere la posibilidad de mitigar aún más las vulnerabilidades de XSS. Por ejemplo, implemente una Directiva de seguridad de contenido (CSP) restrictiva. Para más información, vea Aplicación de una directiva de seguridad de contenido para Blazor de ASP.NET Core.
Para obtener más información, vea Evitar scripting entre sitios (XSS) en ASP.NET Core.
Protección entre orígenes
Los ataques entre orígenes se producen cuando un cliente de un origen diferente realiza una acción en el servidor. La acción malintencionada es normalmente una solicitud GET o un método POST de formulario (falsificación de solicitud entre sitios, CSRF), pero también puede ser la apertura de un WebSocket malintencionado. Las aplicaciones Blazor ofrecen las mismas garantías que cualquier otra aplicación SignalR que use la oferta de protocolo del centro de conectividad:
- Se puede acceder a las aplicaciones desde diferentes orígenes, a menos que se tomen medidas adicionales para evitarlo. Para deshabilitar el acceso entre orígenes, deshabilite el uso compartido de recursos entre orígenes (CORS) en el punto de conexión agregando el middleware de CORS a la canalización y el atributo DisableCorsAttribute a los metadatos del punto de conexión de Blazor, o bien limite el conjunto de orígenes permitidos mediante la configuración de SignalR para el uso compartido de recursos entre orígenes. Para obtener instrucciones sobre las restricciones de origen de WebSocket, consulte Compatibilidad con WebSockets en ASP.NET Core.
- Si CORS está habilitado, podría ser necesario tomar medidas adicionales para proteger la aplicación en función de la configuración de CORS. Si CORS está habilitado globalmente, se puede deshabilitar para el centro de conectividad de BlazorSignalR agregando los metadatos de DisableCorsAttribute a los metadatos del punto de conexión después de llamar a MapBlazorHub en el generador de rutas del punto de conexión.
Para obtener más información, vea Prevención de ataques de falsificación de solicitud entre sitios (XSRF/CSRF) en ASP.NET Core.
Secuestro de clic
El secuestro de clic implica la representación de un sitio como un <iframe>
dentro de otro sitio desde un origen diferente para engañar al usuario y que realice acciones en el sitio que está bajo ataque.
Para proteger una aplicación frente a su representación dentro de un elemento <iframe>
, use la Directiva de seguridad de contenido (CSP) y el encabezado X-Frame-Options
.
Para obtener más información, vea los siguientes recursos:
- Aplicación de una directiva de seguridad de contenido para Blazor de ASP.NET Core
- Documentación web de MDN: X-Frame-Options
Redireccionamientos abiertos
Cuando se inicia una sesión de aplicación, el servidor realiza la validación básica de las direcciones URL que se envían como parte del inicio de la sesión. El marco comprueba que la URL base es un elemento primario de la dirección URL actual antes de establecer el circuito. El marco no realiza ninguna comprobación adicional.
Cuando un usuario selecciona un vínculo en el cliente, la dirección URL del vínculo se envía al servidor, que determina la acción que se debe realizar. Por ejemplo, la aplicación puede realizar una navegación en el lado cliente o indicar al explorador que vaya a la nueva ubicación.
Los componentes también pueden desencadenar solicitudes de navegación de forma programática mediante el uso de NavigationManager. En estos casos, la aplicación puede realizar una navegación del lado cliente o indicar al explorador que vaya a la nueva ubicación.
Los componentes deben:
- Evitar el uso de datos proporcionados por el usuario como parte de los argumentos de la llamada de navegación.
- Validar los argumentos para asegurarse de que la aplicación permite el destino.
De lo contrario, un usuario malintencionado puede forzar al explorador para que vaya a un sitio controlado por un ciberdelincuente. En este escenario, el ciberdelincuente engaña a la aplicación para que use algunos datos proporcionados por el usuario como parte de la invocación del método NavigationManager.NavigateTo.
Los siguientes consejos también se aplican cuando se representan vínculos como parte de la aplicación:
- Si es posible, use vínculos relativos.
- Compruebe que los destinos de los vínculos absolutos son válidos antes de incluirlos en una página.
Para más información, consulte Prevención de ataques de redireccionamiento abierto en ASP.NET Core.
Lista de comprobación de seguridad
La siguiente lista de consideraciones de seguridad no está completa:
- Valide los argumentos de eventos.
- Valide las entradas y los resultados de las llamadas de interoperabilidad de JS.
- Evite usar (o validar previamente) datos proporcionados por el usuario para las llamadas de .NET a interoperabilidad de JS.
- Impida que el cliente asigne una cantidad de memoria ilimitada para:
- Datos dentro del componente.
- Objetos DotNetObjectReference devueltos al cliente.
- Protección contra varios envíos.
- Cancele las operaciones de ejecución prolongada cuando se elimina el componente.
- Evite los eventos que generan grandes cantidades de datos.
- Evite usar datos proporcionados por el usuario como parte de las llamadas a NavigationManager.NavigateTo, pero si es inevitable, valide en primer lugar los datos de direcciones URL proporcionados por el usuario cotejándolos con un conjunto de orígenes permitidos.
- No tome decisiones de autorización basadas en el estado de la interfaz de usuario, solo en el estado del componente.
- Considere la posibilidad de usar la Directiva de seguridad de contenido (CSP) para protegerse frente a ataques XSS. Para más información, vea Aplicación de una directiva de seguridad de contenido para Blazor de ASP.NET Core.
- Considere la posibilidad de usar CSP y X-Frame-Options para protegerse frente al secuestro de clic.
- Asegúrese de que la configuración de CORS sea adecuada al habilitar CORS, o bien deshabilite CORS de forma explícita para las aplicaciones Blazor.
- Realice las pruebas necesarias para asegurarse de que los límites del servidor para la aplicación Blazor proporcionan una experiencia de usuario aceptable sin niveles de riesgo inaceptables.