Compartir vía


Qué no se debe hacer en ASP.NET y qué hacer en su lugar

En este tema se describen varios errores comunes que las personas cometen en los proyectos web de ASP.NET. Proporciona recomendaciones de lo que debe hacer para evitar estos errores comunes. Se basa en una presentación de Damian Edwards en la Conferencia de desarrolladores noruegos.

Declinación de responsabilidades

Este tema no está pensado como una guía completa para asegurarse de que la aplicación es segura y eficaz. Todavía debe seguir los procedimientos recomendados para la seguridad y el rendimiento que no se describen en este tema. Solo sugiere cómo evitar errores comunes relacionados con las clases y procesos de .NET.

Información general

Este tema contiene las siguientes secciones:

Cumplimiento de estándares

Adaptadores de control

Recomendación: Deje de usar adaptadores de control para la representación adaptable y, en su lugar, use consultas multimedia CSS y HTML compatibles con los estándares.

Los adaptadores de controles se introdujeron en .NET 2.0 para representar el código de presentación personalizado para distintos dispositivos y entornos. Ahora, esta representación adaptable se puede realizar con CSS y HTML. Debe dejar de usar adaptadores de control y convertir los adaptadores existentes en CSS y HTML.

Para obtener más información, vea Consultas multimedia y Cómo: Agregar páginas móviles a la aplicación MVC y Web Forms de ASP.NET.

Propiedades de estilo en controles

Recomendación: Deje de configurar valores de estilo en el marcado de control y, en su lugar, establezca valores de formato en hojas de estilos CSS.

Los controles de servidor web contienen docenas de propiedades que se pueden usar para establecer propiedades de estilo en línea. Por ejemplo, la propiedad ForeColor establece el color del texto de un control. Puede lograr este mismo efecto de forma más eficaz a través de hojas de estilos CSS. Las hojas de estilos permiten centralizar los valores de estilo y evitar establecer estos valores a lo largo de la aplicación.

En el ejemplo siguiente se muestra una clase CSS que establece texto en rojo.

.CautionRow {
    color: red;
}

En el ejemplo siguiente se muestra cómo aplicar dinámicamente la clase CSS.

protected void CustomersGridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.Cells[2].Text == "Unconfirmed")
    {
        e.Row.CssClass = "CautionRow";
    }
}

Devoluciones de llamada de página y control

Recomendación: Deje de usar devoluciones de llamada de página y control y, en su lugar, use cualquiera de las siguientes opciones: AJAX, UpdatePanel, métodos de acción MVC, API web o SignalR.

En versiones anteriores de ASP.NET, los métodos de devolución de llamada de página y control le permitieron actualizar parte de la página web sin actualizar la página completa. Ahora, puede realizar actualizaciones parciales de página a través de AJAX, UpdatePanel, MVC, Web API o SignalR. Debe dejar de usar métodos de devolución de llamada porque pueden causar problemas con direcciones URL y enrutamiento descriptivos. De forma predeterminada, los controles no habilitan los métodos de devolución de llamada, pero si ha habilitado esta característica en un control, debe deshabilitarla.

Detección de capacidades del explorador

Recomendación: Deje de usar la detección estática de capacidades del explorador y, en su lugar, use la detección dinámica de características.

En versiones anteriores de ASP.NET, las características admitidas para cada explorador se almacenaban en un archivo XML. La detección de compatibilidad con características a través de una búsqueda estática no es el mejor enfoque. Ahora, puede detectar dinámicamente las características admitidas de un explorador mediante un marco de detección de características, como Modernizr. La detección de características determina la compatibilidad al intentar usar un método o una propiedad y, a continuación, comprobar si el explorador produjo el resultado deseado. De forma predeterminada, Modernizr se incluye en las plantillas de aplicación web.

Seguridad

Validación de solicitudes

Recomendación: Valide la entrada del usuario y codifique la salida de los usuarios.

La validación de solicitudes es una característica de ASP.NET que inspecciona cada solicitud y la detiene si se encuentra una amenaza percibida. No dependa de la validación de solicitudes para proteger la aplicación frente a ataques de scripting entre sitios. En su lugar, valide todas las entradas de los usuarios y codifique la salida. En algunos casos limitados, puede usar expresiones regulares para validar la entrada, pero en casos más complicados debe validar la entrada del usuario mediante clases de .NET que determinan si el valor coincide con los valores permitidos.

En el ejemplo siguiente se muestra cómo usar un método estático en la clase URI para determinar si el URI proporcionado por un usuario es válido.

var isValidUri = Uri.IsWellFormedUriString(passedUri, UriKind.Absolute);

Sin embargo, para comprobar lo suficiente el URI, también debe verificarlo para asegurar que especifica http o https. En el ejemplo siguiente se usan métodos de instancia para comprobar que el URI es válido.

var uriToVerify = new Uri(passedUri);
var isValidUri = uriToVerify.IsWellFormedOriginalString();
var isValidScheme = uriToVerify.Scheme == "http" || uriToVerify.Scheme == "https";

Antes de representar la entrada del usuario como HTML o incluir la entrada del usuario en una consulta SQL, codifique los valores para asegurarse de que no incluye un código malintencionado.

Puede codificar el valor en formato HTML en el marcado con la sintaxis <%: %>, como se muestra a continuación.

<span><%: userInput %></span>

O bien, en la sintaxis de Razor, puede codificar en HTML con @, como se muestra a continuación.

<span>@userInput</span>

En el ejemplo siguiente se muestra cómo codificar en HTML un valor en código subyacente.

var encodedInput = Server.HtmlEncode(userInput);

Para codificar de forma segura un valor para los comandos SQL, use parámetros de comando como SqlParameter.

Autenticación y sesión de formularios sin cookies

Recomendación: Requerir cookies.

Pasar información de autenticación en la cadena de consulta no es seguro. Por lo tanto, requiera cookies cuando la aplicación incluya autenticación. Si la cookie almacena información confidencial, considere la posibilidad de requerir SSL para la cookie.

En el ejemplo siguiente se muestra cómo especificar en el archivo Web.config que la autenticación de formularios requiere una cookie que se transmite a través de SSL.

<authentication mode="Forms">
  <forms loginUrl="member_login.aspx"
    cookieless="UseCookies"
    requireSSL="true"
    path="/MyApplication" />
</authentication>

EnableViewStateMac

Recomendación: Nunca se establece en false.

De forma predeterminada, EnableViewStateMac se establece en true. Incluso si la aplicación no usa el estado de visualización, no establezca EnableViewStateMac en false. Si establece este valor en false, la aplicación será vulnerable al scripting entre sitios.

A partir de ASP.NET 4.5.2, el runtime aplica EnableViewStateMac=true. Incluso si se establece en false, el runtime omite este valor y continúa con el valor establecido en true. Para obtener más información, consulte ASP.NET 4.5.2 y EnableViewStateMac.

En el ejemplo siguiente se muestra cómo establecer EnableViewStateMac en true. No es necesario establecer este valor en true porque es true de forma predeterminada. Sin embargo, si lo ha establecido en false en cualquier página de la aplicación, debe corregir inmediatamente este valor.

<%@ Page language="C#" EnableViewStateMac="true" %>

Confianza media

Recomendación: No dependa de la confianza media (ni de ningún otro nivel de confianza) como límite de seguridad.

La confianza parcial no protege adecuadamente la aplicación y no debe usarse. En su lugar, use plena confianza y aísle las aplicaciones que no son de confianza en grupos de aplicaciones separados. Además, ejecute cada grupo de aplicaciones con una identidad única. Para obtener más información, consulte La confianza parcial de ASP.NET no garantiza el aislamiento de la aplicación.

<appSettings>

Recomendación: No deshabilite la configuración de seguridad en el elemento <appSettings>.

El elemento appSettings contiene muchos valores necesarios para las actualizaciones de seguridad. No debe cambiar ni deshabilitar estos valores. Si debe deshabilitar estos valores al implementar una actualización, vuelva a habilitar inmediatamente después de completar la implementación.

Para obtener más información, consulte Elemento appSettings de ASP.NET.

UrlPathEncode

Recomendación: Use UrlEncode en su lugar.

El método UrlPathEncode se agregó a .NET Framework para resolver un problema de compatibilidad muy específico del explorador. No codifica adecuadamente una dirección URL y no protege la aplicación del scripting entre sitios. Nunca debe usarlo en la aplicación. En su lugar, use UrlEncode.

En el ejemplo siguiente se muestra cómo pasar una dirección URL codificada como un parámetro de cadena de consulta para un control de hipervínculo.

string destinationURL = "http://www.contoso.com/default.aspx?user=test";
NextPage.NavigateUrl = "~/Finish?url=" + Server.UrlEncode(destinationURL);

Rendimiento y fiabilidad

PreSendRequestHeaders y PreSendRequestContent

Recomendación: No use estos eventos con módulos administrados. En su lugar, escriba un módulo IIS nativo para realizar la tarea necesaria. Consulte Creación de módulos HTTP de código nativo.

Puede usar los eventos PreSendRequestHeaders y PreSendRequestContent con módulos IIS nativos.

Advertencia

No use PreSendRequestHeaders y PreSendRequestContent con módulos administrados que implementen IHttpModule. Establecer estas propiedades puede causar problemas con solicitudes asincrónicas. La combinación de Enrutamiento de solicitud de aplicaciones (ARR) y websockets podría provocar excepciones de infracción de acceso que pueden provocar que w3wp se bloquee. Por ejemplo, iiscore!W3_CONTEXT_BASE::GetIsLastNotification+68 in iiscore.dll ha provocado una excepción de infracción de acceso (0xC0000005).

Eventos de página asincrónicos con formularios web

Recomendación: En Web Forms, evite escribir métodos async void para eventos de ciclo de vida de la página y, en su lugar, use Page.RegisterAsyncTask para el código asincrónico.

Cuando se marca un evento de página con async y void, no puede determinar cuándo ha finalizado el código asincrónico. En su lugar, use Page.RegisterAsyncTask para ejecutar el código asincrónico de una manera que le permita realizar un seguimiento de su finalización.

En el ejemplo siguiente se muestra un controlador de clic de botón que contiene código asincrónico. En este ejemplo se incluye leer un valor de cadena de forma asincrónica, que solo se proporciona como ejemplo simplificado de una tarea asincrónica y no como práctica recomendada.

protected void StartAsync_Click(object sender, EventArgs e)
{
    Page.RegisterAsyncTask(new PageAsyncTask(async() =>
    {
        string stringToRead = "Long text value";

        using (StringReader reader = new StringReader(stringToRead))
        {
            string readText = await reader.ReadToEndAsync();
            Result.Text = readText;
        }
    }));
}

Si usa tareas asincrónicas, establezca la plataforma de destino del runtime Http en 4.5 (o posterior) en el archivo Web.config. Al establecer la plataforma de destino en 4.5, se activa el nuevo contexto de sincronización que se agregó en .NET 4.5. Este valor se establece de forma predeterminada en los nuevos proyectos de Visual Studio, pero no se establece si está trabajando con un proyecto existente.

<system.web>
    <httpRuntime TargetFramework="4.5" />
</system.web>

Trabajo de disparo y olvido

Recomendación: Cuando controla una solicitud dentro de ASP.NET, evite iniciar el trabajo de disparo y olvido, (por ejemplo, llamar al método ThreadPool.QueueUserWorkItem o crear un temporizador que llame repetidamente a un delegado).

Si la aplicación tiene un trabajo de disparo y olvido que se ejecuta dentro de ASP.NET, la aplicación puede salir de la sincronización. En cualquier momento, el dominio de la aplicación se puede destruir, lo que significa que el proceso en curso ya no coincide con el estado actual de la aplicación.

Debe mover este tipo de trabajo fuera de ASP.NET. Puede usar un trabajo de web, un servicio de Windows o un rol de trabajo en Azure para realizar el trabajo en curso y ejecutar ese código desde otro proceso.

Si debe realizar este trabajo en ASP.NET, puede agregar el paquete Nuget denominado WebBackgrounder para ejecutar el código.

Cuerpo de la entidad de solicitud

Recomendación: Evite leer Request.Form o Request.InputStream antes del evento ejecutar del controlador.

Lo más temprano que debe leer Request.Form o Request.InputStream es durante el evento ejecutar del controlador. En MVC, el controlador es el que controla y el evento ejecutar es cuando se ejecuta el método de acción. En Web Forms, Page es el controlador y el evento ejecutar es cuando se desencadena el evento Page.Init. Si lee el cuerpo de la entidad de solicitud antes que el evento ejecutar, interfiere con el procesamiento de la solicitud.

Si necesita leer el cuerpo de la entidad de solicitud antes del evento ejecutar, use Request.GetBufferlessInputStream o Request.GetBufferedInputStream. Cuando se usa GetBufferlessInputStream, obtiene la secuencia sin procesar de la solicitud y asume la responsabilidad de procesar toda la solicitud. Después de llamar a GetBufferlessInputStream, Request.Form y Request.InputStream no están disponibles porque no se han rellenado por ASP.NET. Cuando usa GetBufferedInputStream, obtiene una copia de la secuencia de la solicitud. Request.Form y Request.InputStream siguen estando disponibles más adelante en la solicitud porque ASP.NET rellena la otra copia.

Response.Redirect y Response.End

Recomendación: Tenga en cuenta las diferencias en el modo en que se controla el subproceso después de llamar a Response.Redirect(String).

El método Response.Redirect(String) llama al método Response.End. En un proceso sincrónico, llamar a Request.Redirect hace que el subproceso actual se anule inmediatamente. Sin embargo, en un proceso asincrónico, llamar a Response.Redirect no anula el subproceso actual, por lo que la ejecución del código continúa para la solicitud. En un proceso asincrónico, debe devolver la tarea del método para detener la ejecución del código.

En un proyecto de MVC, no debe llamar a Response.Redirect. En su lugar, devuelva RedirectResult.

EnableViewState y ViewStateMode

Recomendación: Use ViewStateMode, en lugar de EnableViewState, para proporcionar un control pormenorizado sobre cuáles controles usan el estado de visualización.

Al establecer EnableViewState en false en la directiva Page, el estado de visualización está deshabilitado para todos los controles de la página y no se puede habilitar. Si desea habilitar el estado de visualización solo para determinados controles de la página, establezca ViewStateMode en Deshabilitado para la página.

<%@ Page ViewStateMode="Disabled" . . . %>

A continuación, establezca ViewStateMode en Habilitado solo en los controles que realmente necesitan estado de visualización.

<asp:GridView ViewStateMode="Enabled" runat="server">

Al habilitar el estado de visualización solo para los controles que lo necesitan, puede reducir el tamaño del estado de visualización de las páginas web.

SqlMembershipProvider

Recomendación: Use Proveedores universales.

En las plantillas de proyecto actuales, SqlMembershipProvider se ha reemplazado por Proveedores universales de ASP.NET, que está disponible como un paquete NuGet. Si usa SqlMembershipProvider en un proyecto que se creó con una versión anterior de las plantillas, debe cambiar a Proveedores universales. Los Proveedores universales trabajan con todas las bases de datos admitidas por Entity Framework.

Para obtener más información, vea Presentación de Proveedores universales de ASP.NET.

Solicitudes de larga duración (>110 segundos)

Recomendación: use WebSockets o SignalR para los clientes conectados y use operaciones asincrónicas de E/S.

Las solicitudes de larga duración pueden provocar resultados imprevisibles y un rendimiento deficiente en la aplicación web. La configuración de tiempo de espera predeterminada para una solicitud es de 110 segundos. Si usa el estado de sesión con una solicitud de ejecución prolongada, ASP.NET liberará el bloqueo en el objeto de sesión después de 110 segundos. Sin embargo, la aplicación podría estar en medio de una operación en el objeto sesión cuando se libera el bloqueo y es posible que la operación no se complete correctamente. Si se bloquea una segunda solicitud del usuario mientras se ejecuta la primera solicitud, la segunda solicitud podría tener acceso al objeto de sesión en un estado incoherente.

Si la aplicación incluye operaciones de E/S de bloqueo (o sincrónicas), no responderá.

Para mejorar el rendimiento, use las operaciones asincrónicas de E/S en .NET Framework. Además, use WebSockets o SignalR para conectar clientes al servidor. Estas características están diseñadas para controlar de forma eficaz las solicitudes de larga duración.