El software empresarial tiende a permanecer en producción durante mucho tiempo. Esto es especialmente cierto para ASP.NET y ASP.NET Core, que son comunes en sistemas empresariales de larga duración, especialmente en entornos centrados en Microsoft. Según las estadísticas, aproximadamente el 40% de las empresas ejecutan al menos una aplicación ASP.NET crítica, y seguirán haciéndolo durante años, por lo que la revisión del código debe tener en cuenta el soporte del framework, la aplicación de parches de seguridad, la mantenibilidad y el riesgo operativo.
No hay duda de que tal longevidad es una fortaleza para las grandes empresas con infraestructuras extremadamente complicadas. Sin embargo, tiene un costo, ya que los sistemas que duran una década acumulan decisiones tomadas bajo viejas restricciones, por desarrolladores que ya se han marchado. Además, el panorama de amenazas era completamente diferente cuando se diseñó el sistema. Por lo tanto, hoy probablemente necesite una revisión de código estructurada para descubrir cómo es realmente su aplicación ASP.NET bajo la superficie. Lo más importante es que debería realizar una antes de que un incidente de seguridad, un despliegue fallido o una crisis de rendimiento descubran debilidades críticas del sistema.
En esta lista de verificación, escrita por desarrolladores de Redwerk con más de 10 años de experiencia, cubriremos qué hace que una revisión de una aplicación web ASP.NET Core sea exhaustiva. Nos hemos asegurado de que sea comprensible tanto para las personas que encargan las revisiones como para los desarrolladores que las ejecutan. Por lo tanto, cada sección explica no solo qué revisar, sino por qué es importante para su negocio a largo plazo.
Preparación previa a la revisión
En primer lugar, siempre debe recordar que una revisión solo es tan buena como la preparación que la respalda. Por lo tanto, el tiempo dedicado a recopilar contexto por adelantado determina si este proceso genera conocimiento o confusión.
Definir alcance y criterios de éxito:
- Aclarar si la revisión cubre la aplicación completa, un servicio o módulo específico, un candidato a pre-lanzamiento, o una base de código heredada que se entrega a un nuevo equipo
- Definir qué es el éxito para usted: una lista priorizada de hallazgos de seguridad, una línea base de rendimiento, preparación para un despliegue en producción, o debida diligencia antes de una adquisición
- Identificar cualquier área problemática conocida por el equipo. Estos podrían ser puntos finales lentos, excepciones recurrentes en los registros, fallos intermitentes en el despliegue o características que nadie quiere tocar
Verificar el entorno y la cadena de herramientas:
- Confirmar que la solución se compila limpiamente tanto en configuraciones Debug como Release, sin advertencias tratadas como errores y sin advertencias suprimidas sin justificación documentada
- Verificar que el proyecto apunta a una versión compatible de .NET. La página del ciclo de vida de soporte de Microsoft es la referencia autorizada que debe usar. Recuerde, ejecutar un tiempo de ejecución de fin de vida útil en producción es un riesgo de cumplimiento y seguridad
- Verificar que dotnet restore se completa sin advertencias de vulnerabilidad. Ejecute dotnet list package –vulnerable y documente cualquier paquete NuGet marcado antes de comenzar la revisión
Revisar documentación e historial reciente:
- La configuración y la incorporación deben estar lo suficientemente bien documentadas como para que un nuevo desarrollador pueda ponerse en marcha de manera predecible, sin depender del conocimiento tribal
- Revisar el historial de commits reciente para comprender qué ha cambiado. Esto es importante porque una corrección de seguridad confirmada la semana pasada merece una atención más cercana que un código estable que no ha cambiado en un año
- Verificar que las solicitudes de extracción abiertas se basan en la rama principal para que la revisión refleje el estado actual del código
Estructura y Arquitectura del Proyecto
ASP.NET Core le da mucha libertad en cómo organizar un proyecto. Esa libertad es valiosa cuando se usa deliberadamente y costosa cuando no se usa en absoluto, así que tenga en cuenta los siguientes puntos.
Separación de responsabilidades:
- Verifique que la aplicación sigue una estructura en capas consistente: los controladores o los puntos finales de API mínimos solo manejan las preocupaciones de HTTP; la lógica de negocio reside en clases de servicio dedicadas. El acceso a datos debe estar claramente separado de las preocupaciones de HTTP. Ya sea que use EF Core directamente en servicios, objetos de consulta o manejadores de estilo CQRS, el enfoque elegido debe aplicarse de manera consistente en toda la base de código.
- Los controladores deben coordinar principalmente las preocupaciones de HTTP y delegar la lógica de negocio. Si las acciones contienen reglas de negocio sustanciales, lógica de persistencia u orquestación, las responsabilidades pueden estar mal ubicadas.
Mala práctica:
[HttpPost("orders")]
public async Task CreateOrder([FromBody] OrderRequest request)
{
// Validate, calculate tax, update inventory, send email — all in the controller
var tax = request.Total * 0.2m;
var finalTotal = request.Total + tax;
var order = new Order { Total = finalTotal, UserId = request.UserId };
await _db.Orders.AddAsync(order);
await _db.SaveChangesAsync();
await _inventoryService.DecrementAsync(request.Items);
await _emailService.SendConfirmationAsync(request.UserEmail, order.Id);
return Ok(order);
}
Buena práctica:
[HttpPost("orders")]
public async Task CreateOrder([FromBody] OrderRequest request)
{
var result = await _orderService.CreateAsync(request);
return result.IsSuccess ? Ok(result.Value) : BadRequest(result.Error);
}
Inyección de dependencias:
- Verifique que todos los servicios estén registrados en el contenedor de inyección de dependencias y que se use la inyección de constructor de manera consistente, ya que resolver servicios manualmente desde el contenedor dentro de los métodos es una señal de que la arquitectura está luchando contra su propio diseño
- Verifique que los ciclos de vida de los servicios sean apropiados: transitorios para operaciones ligeras y sin estado. Con ámbito para servicios que comparten estado dentro de una solicitud (como DbContext), y singleton solo para servicios que son genuinamente seguros para subprocesos y sin estado
Organización de proyectos y soluciones:
- Confirme que la estructura del proyecto refleja los límites arquitectónicos. Un proyecto llamado MyApp.Core que contiene tanto la lógica de dominio como los modelos de base de datos tiene un problema de nomenclatura que generalmente indica un problema organizativo debajo de él
- Verifique que no existan referencias circulares de proyecto. Técnicamente son posibles en algunas configuraciones, pero siempre indican un diseño de dependencia que necesita ser desenredado
Pipeline de Middleware y Manejo de Solicitudes
La columna vertebral de toda aplicación ASP.NET Core es su pipeline de middleware. Por lo tanto, el orden en que se registran los componentes de middleware afecta directamente no solo su rendimiento y comportamiento, sino también la seguridad. Un cuerpo humano no puede funcionar correctamente con una columna vertebral comprometida, y lo mismo ocurre con una aplicación ASP.NET. Un orden incorrecto podría romper silenciosamente la autenticación, filtrar excepciones a los clientes o agregar procesamiento innecesario a cada solicitud.
Orden de middleware:
- Verifique que UseExceptionHandler o UseDeveloperExceptionPage estén registrados primero en el pipeline, ya que el middleware de manejo de excepciones debe envolver todo lo demás para capturar errores de componentes posteriores
- Confirme que los pipelines de producción siguen el orden de middleware recomendado por Microsoft: manejo de excepciones al principio, HSTS en entornos que no sean de desarrollo, redirección HTTPS antes del manejo de solicitudes, luego archivos estáticos si es necesario, enrutamiento, autenticación y autorización. El middleware mal ordenado puede llevar a un comportamiento de seguridad incorrecto o a un procesamiento de solicitudes innecesario
- Verifique que la autenticación (UseAuthentication) venga antes que la autorización (UseAuthorization). Invertir el orden significa que las decisiones de autorización se toman antes de que se establezca la identidad del usuario, lo que seguramente causará problemas más adelante
- Revise todo el pipeline para ver si hay componentes de middleware registrados en el orden incorrecto o registrados más de una vez, ya que el middleware redundante agrega latencia sin beneficio
Validación de solicitudes:
- Verifique que la validación del modelo se aplique de manera consistente. En los controladores marcados con [ApiController], ASP.NET Core devuelve automáticamente errores de validación para modelos inválidos. En controladores MVC, Razor Pages, Minimal APIs o pipelines personalizados, confirme que la validación se maneja a través de filtros, filtros de puntos finales, FluentValidation o comprobaciones explícitas donde sea apropiado
- Confirme que los límites de tamaño de solicitud se configuran apropiadamente. La configuración predeterminada de MultipartBodyLengthLimit y MaxRequestBodySize no siempre es adecuada para cargas de trabajo de producción y debe establecerse explícitamente según los requisitos de la aplicación
Manejo de respuestas:
- Verifique que las respuestas incluyan encabezados apropiados de control de caché para el contenido que debe o no debe ser almacenado en caché por los navegadores y las CDN
- Verifique que la compresión de respuesta esté habilitada para tipos de contenido basados en texto. La compresión Brotli y Gzip reduce significativamente el ancho de banda para APIs pesadas en JSON
Autenticación y Autorización
Debe tener en cuenta que los problemas de autenticación y autorización representan una parte significativa de los incidentes de seguridad del mundo real en aplicaciones web. Para comprender mejor cuán crucial es esto, considere que en octubre de 2025, Microsoft corrigió CVE-2025-55315, una vulnerabilidad de “HTTP request smuggling” en el servidor Kestrel, que recibió la puntuación CVSS más alta jamás asignada a un problema de ASP.NET Core: 9.9 sobre 10.
Microsoft describió CVE-2025-55315 como una “vulnerabilidad de HTTP request smuggling en Kestrel que podría permitir a un atacante ocultar una solicitud dentro de otra”. En pocas palabras, dependiendo del despliegue y el comportamiento de la aplicación, este problema podría afectar las decisiones de autenticación y autorización. Otra vulnerabilidad que creó fue abrir un camino para la manipulación de solicitudes. El incidente ilustra por qué la autenticación y la autorización deben aplicarse de manera consistente en todas las rutas de solicitud y configuraciones de despliegue. Recuerde que las vulnerabilidades de infraestructura pueden amplificar las debilidades en el control de acceso a nivel de aplicación.
Implementación de autenticación:
- Verifique que la aplicación utiliza ASP.NET Core Identity, Azure Active Directory o una biblioteca establecida de OAuth 2.0 / OpenID Connect en lugar de una implementación de autenticación personalizada, porque los esquemas de autenticación personalizados introducen riesgo sin proporcionar un beneficio significativo sobre las soluciones maduras ya disponibles
- Verifique que la configuración de JWT especifica explícitamente los algoritmos de firma permitidos. Los parámetros de validación de tokens deben validar el emisor, la audiencia, la vida útil y la clave de firma. Si la aplicación restringe los algoritmos de firma permitidos, asegúrese de que solo se acepten los algoritmos esperados y se rechacen los tokens sin firmar
// Explicit token validation — never rely on defaults alone
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = configuration["Jwt:Issuer"],
ValidateAudience = true,
ValidAudience = configuration["Jwt:Audience"],
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
};
});
- Confirme que la lógica de actualización de tokens maneja la expiración de manera elegante y que los tokens expirados se eliminan del almacenamiento del cliente. Un token que era válido hace seis meses no debe seguir otorgando acceso
Implementación de autorización:
- Verifique que la autorización se aplica a nivel de controlador o punto final, y no solo se comprueba dentro de los cuerpos de los métodos de acción. Las comprobaciones de autorización en línea son fáciles de olvidar y imposibles de auditar sistemáticamente
- Confirme que la autorización basada en roles y políticas se utiliza de manera consistente y evite mezclar comprobaciones ad hoc de User.IsInRole() dispersas por la base de código con definiciones formales de políticas
- Compruebe si hay referencias directas de objetos inseguras: los puntos finales que aceptan un ID de registro en la URL deben verificar que el usuario autenticado tiene permiso para acceder a ese registro específico, no solo que ha iniciado sesión
Vulnerable: cualquier usuario autenticado puede acceder a todas las facturas
[HttpGet("invoices/{id}")]
[Authorize]
public async Task GetInvoice(int id)
{
var invoice = await _db.Invoices.FindAsync(id);
return Ok(invoice);
}
Correcto: limite la consulta a los datos propios del usuario autenticado
[HttpGet("invoices/{id}")]
[Authorize]
public async Task GetInvoice(int id)
{
var userId = User.GetUserId();
var invoice = await _db.Invoices
.FirstOrDefaultAsync(i => i.Id == id && i.OwnerId == userId);
return invoice is null ? NotFound() : Ok(invoice);
}
Acceso a Datos y Entity Framework Core
La base de datos es donde nacen la mayoría de los problemas de rendimiento de las aplicaciones ASP.NET. La mayoría de los problemas en esta área provienen de consultas lentas, carga de datos sin seguimiento, consultas N+1 y llamadas síncronas a la base de datos en hilos de solicitud. Estos problemas no solo son debilitantes para su producto, sino que también son bastante costosos de solucionar. Por lo tanto, identificarlos es uno de los beneficios más valiosos de una revisión de código.
Eficiencia de consultas:
- Busque patrones de consulta N+1: una consulta que carga una lista de entidades y luego las recorre para cargar datos relacionados para cada una está ejecutando una consulta para obtener N registros y luego N consultas adicionales para cargar sus relaciones, lo que degrada significativamente el rendimiento a medida que el volumen de datos crece.
Mala práctica:
// Loads all orders, then queries the database once per order to load the customer
var orders = await _db.Orders.ToListAsync();
foreach (var order in orders)
{
order.Customer = await _db.Customers.FindAsync(order.CustomerId); // N extra queries
}
Buena práctica:
// Single query with a join — one database round trip
var orders = await _db.Orders
.Select(o => new OrderDto
{
Id = o.Id,
Total = o.Total,
CustomerName = o.Customer.Name
})
.ToListAsync();
- Verifique que las consultas de solo lectura usen AsNoTracking(). Según la documentación de EF Core de Microsoft, las consultas sin seguimiento devuelven resultados de manera más eficiente porque EF Core no necesita mantener el estado de seguimiento de cambios para objetos que nunca se actualizarán
- Verifique que las proyecciones usen Select() para recuperar solo las columnas que el punto final realmente necesita, en lugar de cargar grafos de entidades completos y descartar la mayoría de los datos
Acceso asíncrono a la base de datos:
- Confirme que todas las llamadas de EF Core utilizan sus variantes asíncronas: ToListAsync(), FirstOrDefaultAsync(), SaveChangesAsync(), etc. La documentación de mejores prácticas de ASP.NET Core de Microsoft es explícita en que las llamadas de bloqueo como .Result y .Wait() en operaciones de base de datos asíncronas pueden agotar el grupo de hilos bajo carga
- Verifique que las instancias de DbContext no se compartan entre hilos ni se almacenen como singletons. DbContext no es seguro para subprocesos, por lo que su ciclo de vida debe tener el ámbito de una sola solicitud
Higiene de esquemas y migraciones:
- Confirme que las migraciones de la base de datos se guardan en el control de versiones y que el historial de migraciones es lineal, ya que las lagunas o conflictos en el historial de migraciones causan fallos en el despliegue
- Verifique que las migraciones se aplican a través de un proceso de despliegue controlado, como scripts de migración revisados, pasos de despliegue CI/CD o un procedimiento de lanzamiento aprobado. Debe evitar depender de migraciones manuales ejecutadas por desarrolladores o cambios automáticos de producción no revisados. Los pasos de migración manuales son el tipo de cosas que se olvidan en el peor momento posible
- Revise los índices para confirmar que las columnas utilizadas en las cláusulas WHERE, las condiciones JOIN y las expresiones ORDER BY tienen índices apropiados. La falta de índices es la causa más común de consultas que funcionan bien en desarrollo y se desmoronan en producción
Diseño de API e Higiene de Contratos
Una API de ASP.NET Core es un contrato entre el servidor y cada cliente que depende de ella. Por lo tanto, romper ese contrato, incluso accidentalmente, puede causar fallos que a menudo son invisibles durante el desarrollo. Sin embargo, serían extremadamente dolorosos cuando estos problemas lleguen a producción.
Versionado:
- Verifique que la API utiliza una estrategia de versionado consistente, ya sea versionado de ruta de URL (/api/v1/), versionado de cadena de consulta o versionado basado en encabezados. El enfoque específico que elija importa menos que simplemente tener uno y aplicarlo consistentemente
- Confirme que las versiones de API obsoletas están claramente marcadas y que se comunica un cronograma de migración a los consumidores. Eliminar un punto final sin previo aviso es una forma rápida de romper integraciones
Validación de entrada y modelado de respuestas:
- Verifique que todos los modelos de solicitud estén anotados con atributos de anotación de datos o reglas de Fluent Validation, y que la validación se aplique globalmente a través de un filtro en lugar de verificarse individualmente en cada acción
- Confirme que las respuestas de la API utilizan un formato de sobre consistente; las formas de respuesta inconsistentes requieren que los clientes manejen cada punto final de manera diferente y hacen que el manejo de errores sea frágil
- Verifique que las respuestas de error devuelvan códigos de estado HTTP apropiados y mensajes de error significativos, ya que devolver un 200 OK con un campo de error en el cuerpo es un patrón que rompe la semántica HTTP y confunde a los clientes
OpenAPI y documentación:
- Verifique que Swagger o una herramienta de documentación OpenAPI compatible esté configurada y produzca documentación precisa y actualizada. Tenga en cuenta que la documentación de API desactualizada es casi peor que ninguna documentación porque engaña activamente a los consumidores
- Confirme que los puntos finales de API públicos y los modelos de solicitud/respuesta consumidos externamente estén documentados lo suficientemente claramente como para que la documentación OpenAPI generada sea útil. Priorice los contratos públicos, los campos no obvios, las respuestas de error, los requisitos de autenticación y el comportamiento de versionado. Estos se alimentan directamente en la documentación generada y no requieren esfuerzo adicional una vez que el hábito está establecido
Mejores Prácticas de Seguridad
Definitivamente necesita consultar la OWASP DotNet Security Cheat Sheet, mantenida específicamente para aplicaciones ASP.NET. Sin embargo, lo más importante es que debe tratarla como una línea base, no como el techo de sus prácticas de seguridad. Los elementos a continuación se derivan de los problemas que encontramos con mayor frecuencia durante las revisiones de bases de código ASP.NET de producción.
Prevención de inyecciones:
- Revise todas las consultas de bases de datos en busca de concatenación de cadenas; cualquier consulta que construya SQL anexando la entrada del usuario es vulnerable a la inyección de SQL, sin importar cuán improbable parezca la ruta de explotación. Las consultas parametrizadas y la interfaz LINQ de EF Core son las herramientas correctas
- Verifique los riesgos de inyección de comandos en cualquier código que pase valores proporcionados por el usuario a Process.Start, comandos de shell o ejecución de código dinámico
Cross-Site Scripting (XSS) y Cross-Site Request Forgery (CSRF):
- Confirme que las vistas de Razor utilizan la sintaxis de codificación @ de manera consistente y que la representación de HTML sin procesar a través de Html.Raw() se utiliza solo donde sea estrictamente necesario y con contenido completamente confiable
- Para flujos de navegador autenticados con cookies, verifique que la protección contra falsificaciones se aplique a las sumisiones de formularios que cambian el estado y a los métodos HTTP inseguros relevantes. Para las APIs con tokens Bearer, evalúe el riesgo de CSRF por separado. CSRF es principalmente una preocupación cuando los navegadores adjuntan credenciales automáticamente, como cookies o autenticación básica
Encabezados de seguridad HTTP:
- Confirme que los encabezados de seguridad se establecen a través de middleware o un filtro personalizado: Content-Security-Policy, X-Content-Type-Options, X-Frame-Options, Referrer-Policy y Strict-Transport-Security son la línea base
- Verifique que HTTPS se aplique en producción y que UseHsts esté configurado con un valor max-age apropiado. HSTS le dice a los navegadores que siempre usen HTTPS para su dominio, incluso si el usuario escribe HTTP manualmente
Exposición de datos sensibles:
- Asegúrese de que los secretos de producción, las claves de API, las claves de firma y las cadenas de conexión privilegiadas no se guarden en el control de versiones. Los valores predeterminados locales no sensibles pueden residir en archivos de configuración, pero los secretos reales deben provenir de variables de entorno, almacenes de secretos, identidad administrada o configuración en tiempo de despliegue. Pertenecen a variables de entorno, Azure Key Vault o algún otro sistema de gestión de secretos
- Verifique que los datos sensibles no se registren; una entrada de registro que captura el cuerpo completo de una solicitud puede persistir inadvertidamente contraseñas, números de tarjetas de crédito o datos personales mucho después de que se haya procesado la solicitud
- Confirme que los detalles de las excepciones nunca se envían a los clientes en producción. UseDeveloperExceptionPage de ASP.NET Core debe restringirse al entorno de desarrollo
Patrones Asíncronos y Rendimiento
ASP.NET Core está construido para alta concurrencia por defecto, que es una de las razones de su popularidad para sistemas de nivel empresarial. Sin embargo, esa concurrencia se socava cada vez que una operación síncrona bloquea un hilo de solicitud. Una sola llamada bloqueante en una ruta de código activa puede reducir el rendimiento en un orden de magnitud bajo carga. Eso es algo que siempre debe tener en cuenta.
Evitar el bloqueo síncrono:
- Busque .Result, .Wait() y .GetAwaiter().GetResult() en Task. En ASP.NET Core, estas llamadas bloquean los hilos de solicitud y pueden contribuir a la inanición del grupo de hilos, bajo rendimiento y picos de latencia bajo carga. Es mejor preferir asíncrono en toda la pila de llamadas
- Confirme que no se usa async void en acciones de controlador o métodos de servicio. Evite async void fuera de los controladores de eventos. Los métodos async void no se pueden esperar, las excepciones son difíciles de observar y manejar correctamente, y los llamadores no pueden saber cuándo ha finalizado la operación
ThreadPool e IHttpClientFactory:
- Verifique que HttpClient no se instancia directamente con new HttpClient() dentro de los métodos. Prefiera IHttpClientFactory para llamadas HTTP salientes, especialmente cuando los clientes necesitan configuración nombrada/con tipo, políticas de resiliencia, registro o ciclos de vida de manejadores controlados. Además, verifique que el código no crea instancias de HttpClient de corta duración por solicitud, lo que puede agotar los sockets bajo carga
- Busque operaciones intensivas en CPU que se ejecuten en hilos de solicitud. Cargas de trabajo como procesamiento de imágenes, generación de PDF o exportaciones de grandes volúmenes de datos deben descargarse a servicios en segundo plano o colas de mensajes en lugar de bloquear el hilo de solicitud HTTP
Caché de respuestas y caché de salida:
- Identifique puntos finales que sirven repetidamente los mismos datos y verifique que la caché de respuestas o salidas esté implementada para ellos. Un punto final que accede a la base de datos en cada solicitud para devolver datos que cambian una vez por hora está desperdiciando recursos
- Confirme que existe una lógica de invalidación de caché y que es correcta, ya que una caché que crece indefinidamente o que sirve datos obsoletos después de una escritura es más difícil de depurar que ninguna caché en absoluto
Gestión de Configuración y Secretos
La configuración del sistema es la causa subyacente de la brecha entre cómo se comporta una aplicación en desarrollo y en producción. Por lo tanto, es esencial que la examine cuidadosamente durante la revisión del código para identificar cualquier problema que pueda generar fugas de datos devastadoras.
Configuración específica del entorno:
- Confirme que todos los valores específicos del entorno provienen de archivos appsettings.{Environment}.json o variables de entorno, no de valores codificados en el código fuente. Una cadena de conexión que apunta a una base de datos local que llega a un despliegue de producción causará una interrupción
- Verifique que la aplicación valida la configuración requerida al inicio y falla ruidosamente con un error descriptivo si algo falta. Una aplicación parcialmente configurada que se inicia correctamente pero falla al primer uso es más difícil de diagnosticar que una que se niega a iniciar
// Fail at startup, not at runtime
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException(
"Required configuration 'ConnectionStrings:DefaultConnection' is missing.");
- Verifique que los archivos appsettings.Development.json estén en .gitignore si contienen credenciales reales, incluso las de desarrollo. Las credenciales de desarrollador guardadas en el control de versiones tienen un largo historial de aparecer en producción a través de copiar y pegar o configuraciones erróneas. Vea lo que podría llevar en uno de nuestros artículos sobre fugas de datos
Secretos en producción:
- Verifique que los secretos de producción se gestionan a través de Azure Key Vault, AWS Secrets Manager, variables de entorno inyectadas en el momento del despliegue o un mecanismo equivalente
- Confirme que la aplicación utiliza identidades administradas o federación de identidades de carga de trabajo para acceder a servicios de Azure en lugar de cadenas de conexión con credenciales incrustadas, ya que las identidades administradas eliminan toda una categoría de problemas de rotación y fuga de secretos
Pruebas y Cobertura de Código
Una cosa crucial que hay que recordar es que es imposible verificar que la base de código es estable sin pruebas. No hay duda de que los sistemas no probados tienen inestabilidades y vulnerabilidades que aparecerían en el peor momento posible.
Pruebas unitarias y de integración:
- Verifique que la lógica de negocio crítica, los casos límite y las rutas de fallo estén cubiertos por pruebas automatizadas
- Confirme que las pruebas utilizan inyección de dependencias y mocking (Moq, NSubstitute) para aislar el código bajo prueba de bases de datos reales, APIs externas y sistemas de archivos. Tenga en cuenta que las pruebas que requieren una base de datos activa son pruebas de integración y deben tratarse de manera diferente a las pruebas unitarias
- Verifique que WebApplicationFactory<T> se utiliza para pruebas de integración en lugar de iniciar entornos de despliegue completos. Proporciona un host de prueba ligero en proceso que ejecuta todo el pipeline de middleware sin requerir un servidor desplegado
Calidad de las pruebas:
- Revise los nombres de las pruebas para confirmar que describen el comportamiento en lugar de los nombres de los métodos: CreateOrder_WhenInventoryIsInsufficient_ShouldReturnBadRequest es más útil que TestCreateOrder
- Verifique que el pipeline de CI bloquea las fusiones en pruebas fallidas y que ninguna prueba está deshabilitada con [Ignore] o Skip sin una razón rastreada
- Confirme que las rutas críticas se prueban y que la cobertura se rastrea. Los porcentajes brutos por sí solos no garantizan la calidad: un conjunto de pruebas superficiales puede alcanzar el 80% de cobertura al tiempo que omite la lógica que realmente importa. Para sistemas críticos, aplicar un umbral mínimo proporciona una red de seguridad útil, pero el objetivo es una cobertura significativa de la lógica de negocio, casos límite y rutas de fallo
Registro, Observabilidad y Manejo de Errores
Imagine una situación, recibe una llamada a las 2 AM porque algo salió mal en producción y todo el sistema colapsó. El nivel de su pánico en este caso debe ser determinado por la calidad de sus registros. La información que obtiene de ellos a menudo marca la diferencia entre una solución de diez minutos y una investigación de tres horas. Por lo tanto, aplicar la observabilidad y el registro consistente es una base imprescindible para cualquier sistema de nivel empresarial, independientemente del lenguaje de programación o el framework que utilice.
Registro estructurado:
- Verifique que la aplicación utiliza ILogger<T> de la abstracción incorporada Microsoft.Extensions.Logging en lugar de bibliotecas de registro estáticas o llamadas dispersas a Console.WriteLine. La abstracción incorporada se integra con Serilog, NLog y Azure Application Insights a través de la configuración en lugar de cambios de código
- Confirme que las entradas de registro utilizan propiedades estructuradas en lugar de interpolación de cadenas; logger.LogInformation(“Processing order {OrderId} for user {UserId}”, orderId, userId) produce una entrada de registro consultable, mientras que logger.LogInformation($”Processing order {orderId} for user {userId}”) produce una cadena plana que no se puede consultar
Manejo global de excepciones:
- Verifique que se configura un middleware global de manejo de excepciones y que devuelve respuestas de error consistentes y saneadas, ya que las diferentes partes de la aplicación no deberían manejar las excepciones de manera diferente
- Confirme que las excepciones no controladas se registran con suficiente contexto seguro para reconstruir lo que sucedió: ruta de solicitud, ID de correlación, identificador de usuario autenticado cuando corresponda y pila de llamadas. Evite registrar cuerpos de solicitud sin procesar, credenciales, tokens, datos de pago o datos personales a menos que estén explícitamente protegidos y justificados
- Verifique que los errores operativos (fallos de validación, recursos no encontrados, violaciones de reglas de negocio) devuelvan respuestas 4xx apropiadas y se distingan de los errores inesperados que devuelven respuestas 5xx. Tratar cada excepción como un 500 hace imposible separar los errores de las condiciones de error esperadas
Comprobaciones de estado y monitoreo:
- Verifique que los puntos finales de comprobación de estado estén configurados utilizando las comprobaciones de estado incorporadas de ASP.NET Core. Estos deben validar que la aplicación puede conectarse a su base de datos, dependencias externas y cualquier infraestructura crítica
- Confirme que Application Insights, Datadog o una herramienta APM equivalente está configurada en producción. Los registros estructurados y las comprobaciones de estado son necesarios, pero el rastreo distribuido a través de los límites del servicio requiere herramientas dedicadas
¿Por qué confiar en Redwerk para la Revisión de Código ASP.NET?
La Encuesta de Desarrolladores de Stack Overflow confirmó que ASP.NET Core se ubicó entre los diez frameworks web más utilizados a nivel mundial, con el 19.1% de los desarrolladores profesionales trabajando activamente con él. Esto demuestra que ASP.NET Core es un framework maduro y capaz, y las aplicaciones construidas sobre él pueden servir a millones de usuarios, ejecutarse en industrias reguladas y permanecer en producción durante una década o más.
Sin embargo, la lista de verificación anterior existe porque la madurez del framework no protege una base de código de las decisiones tomadas durante el desarrollo. Cosas como una verificación de autenticación faltante, una llamada de base de datos bloqueante en una ruta activa, secretos guardados en el control de versiones, o un pipeline de middleware ensamblado en el orden incorrecto conducen a problemas graves que podrían, potencialmente, desmantelar todo el sistema.
Echemos un vistazo a un ejemplo práctico de cómo se ve este sistema hecho bien: Current, un SaaS de e-gobierno que construimos para agencias de bienestar en todo Estados Unidos. La plataforma se ejecuta en .NET y Azure, maneja datos confidenciales de ciudadanos a través de múltiples agencias gubernamentales de EE. UU. y requirió el cumplimiento del 100% de ADA desde el primer día.
Mientras construíamos para ese entorno, tuvimos que obtener cada elemento de esta lista de verificación correcto antes de que una sola línea de código llegara a producción. Por lo tanto, los desarrolladores y probadores de Redwerk se aseguraron de que la autorización estuviera estrictamente limitada a los datos de cada agencia y que HTTPS se aplicara en todo momento. También implementamos registros estructurados que cumplían con los requisitos de auditoría y comprobaciones de estado que permitían a los equipos de operaciones verificar cada dependencia sin tocar la aplicación. La plataforma ahora es utilizada por divisiones de bienestar en todo el país.
Nuestro equipo de desarrollo ASP.NET ha estado trabajando con la pila de Microsoft desde 2005. Por lo tanto, cuando revisamos una base de código, cubrimos la imagen completa, que incluye:
- Arquitectura
- Seguridad
- Patrones de acceso a datos
- Corrección asíncrona
- Higiene de configuración
- Cobertura de pruebas
Cuando se completa la revisión, recibe un informe específico, priorizado por gravedad y escrito para que tanto los responsables de la toma de decisiones como los desarrolladores puedan actuar sobre él. Si su aplicación ASP.NET necesita una revisión más detallada, nuestro servicio de revisión de código es el lugar para comenzar. Cuéntenos qué ha construido y le diremos qué encontramos.
Vea cómo funciona una revisión de código por sí mismo: más de 80 mejoras identificadas y riesgos de seguridad para un mercado móvil