C# en acción: cómo proteger el código de inyecciones SQL y ataques XSS

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Prefacio: por qué la seguridad no es una opción, sino una higiene básica

\n\n

Imagine: está escribiendo un servicio web genial en C#. Todo funciona perfectamente: los usuarios se registran, dejan comentarios, buscan productos. Y luego llega el "hacker Vasya" (que en realidad solo copió un script de internet) y se roba toda la base de datos. O, peor aún, inserta en su página un script que roba las cookies de todos los visitantes.

\n\n

¿Suena como el guión de una película de terror? Pues esto ocurre todos los días. Según las estadísticas de OWASP, las inyecciones (SQL, XSS, LDAP) se encuentran entre las tres vulnerabilidades más peligrosas de las aplicaciones web. Y C#, con toda su fiabilidad, no le otorga un escudo de inmunidad por defecto.

\n\n

En este artículo, analizaremos los dos ataques más candentes: inyecciones SQL y XSS (Cross-Site Scripting). No solo obtendrá teoría, sino técnicas de defensa prácticas en C# con ejemplos de código reales. ¡Allá vamos!

\n\n

Inyecciones SQL: cuando el usuario se convierte en hacker

\n\n

Cómo se ve en la práctica

\n\n

Supongamos que tiene un inicio de sesión simple. El usuario ingresa su nombre de usuario y contraseña, y usted forma la consulta así:

\n\n
string login = txtLogin.Text;\nstring password = txtPassword.Text;\nstring query = "SELECT * FROM Users WHERE Login = '" + login + "' AND Password = '" + password + "'";\nSqlCommand cmd = new SqlCommand(query, connection);
\n\n

¿Parece lógico? ¡Error! Si un atacante ingresa en el campo de usuario: ' OR 1=1 --, la consulta se convierte en:

\n\n
SELECT * FROM Users WHERE Login = '' OR 1=1 --' AND Password = '...'
\n\n

Todo lo que está después de -- es un comentario. La condición 1=1 siempre es verdadera. La consulta devolverá todos los usuarios y la autenticación se habrá completado. ¿Y si en lugar del comentario inserta DROP TABLE Users? Adiós, base de datos.

\n\n

Protección n.º 1: consultas parametrizadas (su principal escudo)

\n\n

La mejor protección contra las inyecciones SQL es nunca concatenar cadenas manualmente. Use parámetros. El servidor SQL escapará los caracteres peligrosos por sí mismo, y su base de datos recibirá los datos como valores, no como código.

\n\n
string login = txtLogin.Text;\nstring password = txtPassword.Text;\n\nstring query = "SELECT * FROM Users WHERE Login = @Login AND Password = @Password";\n\nusing (SqlConnection conn = new SqlConnection(connectionString))\n{\n    SqlCommand cmd = new SqlCommand(query, conn);\n    cmd.Parameters.AddWithValue("@Login", login);\n    cmd.Parameters.AddWithValue("@Password", password);\n    \n    conn.Open();\n    SqlDataReader reader = cmd.ExecuteReader();\n    // procesamiento del resultado\n}
\n\n

Observe: @Login y @Password son parámetros. Incluso si el usuario ingresa '; DROP TABLE Users; --, el DBMS lo interpretará como una cadena normal, no como código. Seguro y elegante.

\n\n

Protección n.º 2: procedimientos almacenados

\n\n

Otra herramienta potente son los procedimientos almacenados. No solo protegen contra inyecciones, sino que también mejoran el rendimiento (los planes de consulta se almacenan en caché).

\n\n
-- SQL Server\nCREATE PROCEDURE GetUserByLogin\n    @Login NVARCHAR(50)\nAS\nBEGIN\n    SELECT * FROM Users WHERE Login = @Login\nEND
\n\n
// Código C#\nusing (SqlConnection conn = new SqlConnection(connectionString))\n{\n    SqlCommand cmd = new SqlCommand("GetUserByLogin", conn);\n    cmd.CommandType = CommandType.StoredProcedure;\n    cmd.Parameters.AddWithValue("@Login", inputLogin);\n    \n    conn.Open();\n    // ...\n}
\n\n

Protección n.º 3: Entity Framework y ORM

\n\n

Si usa Entity Framework (o cualquier otro ORM), ya está parcialmente protegido. EF genera consultas parametrizadas automáticamente. Pero hay un detalle: las consultas SQL sin procesar a través de EF (método FromSqlRaw) son la misma concatenación. ¡Esté atento!

\n\n
// MALO:\nvar users = context.Users.FromSqlRaw("SELECT * F                

Blogs

Book Recommendations