Seguridad de aplicaciones C#: top 10 vulnerabilidades para principiantes
\n\nDesarrollar aplicaciones seguras en C# no es solo una tendencia, sino una necesidad. Cada día se descubren miles de vulnerabilidades en el mundo, y tu aplicación podría ser el próximo objetivo. En este artículo analizaremos las 10 vulnerabilidades más comunes en aplicaciones C# (.NET) que enfrentan los desarrolladores principiantes. Aprenderás cómo detectarlas y, lo más importante, cómo proteger tu código.
\n\n1. Inyección SQL (SQL Injection)
\n\nLa vulnerabilidad más antigua y aún peligrosa. Ocurre cuando la entrada del usuario se inserta directamente en una consulta SQL.
\n\nCódigo vulnerable:
\nstring userName = Request["username"];\nstring query = "SELECT * FROM Users WHERE Name = '" + userName + "'";\nSqlCommand cmd = new SqlCommand(query, connection);\n\nProtección: Usa consultas parametrizadas o Entity Framework.
\nstring userName = Request["username"];\nstring query = "SELECT * FROM Users WHERE Name = @name";\nSqlCommand cmd = new SqlCommand(query, connection);\ncmd.Parameters.AddWithValue("@name", userName);\n\n2. Cross-Site Scripting (XSS)
\n\nLos ataques XSS permiten a un atacante inyectar un script malicioso en una página que se muestra a otros usuarios.
\n\nCódigo vulnerable:
\nstring comment = Request["comment"];\nResponse.Write("<div>" + comment + "</div>");\n\nProtección: Siempre codifica la salida. Usa HttpUtility.HtmlEncode():
string comment = Request["comment"];\nResponse.Write("<div>" + HttpUtility.HtmlEncode(comment) + "</div>");\n\n3. Falta de autenticación y autorización
\n\nMuchos principiantes olvidan verificar los permisos de acceso a los recursos. Por ejemplo, un usuario podría acceder al perfil de otro simplemente cambiando el ID en la URL.
\n\nCódigo vulnerable:
\npublic IActionResult Profile(int userId)\n{\n var user = _context.Users.Find(userId);\n return View(user);\n}\n\nProtección: Siempre verifica que el usuario actual tenga derecho al recurso solicitado:
\npublic IActionResult Profile(int userId)\n{\n var currentUserId = User.FindFirst(ClaimTypes.NameIdentifier).Value;\n if (userId != currentUserId) return Forbid();\n var user = _context.Users.Find(userId);\n return View(user);\n}\n\n4. Fuga de datos confidenciales a través de excepciones
\n\nLos desarrolladores suelen mostrar información completa de errores, incluyendo la traza de pila y cadenas de conexión.
\n\nCódigo vulnerable:
\ntry\n{\n // código\n}\ncatch (Exception ex)\n{\n Response.Write(ex.ToString()); // ¡PELIGROSO!\n}\n\nProtección: En producción, usa mensajes de error genéricos y registra los detalles en un lugar seguro:
\ntry\n{\n // código\n}\ncatch (Exception ex)\n{\n _logger.LogError(ex, "Error al procesar la solicitud");\n return StatusCode(500, "Ocurrió un error interno del servidor");\n}\n\n5. Almacenamiento inseguro de contraseñas
\n\nAlmacenar contraseñas en texto plano o usando hashes débiles (MD5, SHA1) es un grave error.
\n\nCódigo vulnerable:
\nstring passwordHash = MD5.Hash(password); // ¡NO SE DEBE HACER!\n\nProtección: Usa algoritmos modernos de hash con sal, como BCrypt o ASP.NET Core Identity:
var passwordHasher = new PasswordHasher<IdentityUser>();\nstring passwordHash = passwordHasher.HashPassword(user, password);\n\n6. Protección insuficiente contra CSRF (Cross-Site Request Forgery)
\n\nAtaque en el que un atacante obliga al navegador de la víctima a realizar una acción no deseada en otro sitio, donde