Saltar al pie de página
Iron Academy Logo
Integración de bases de datos en C#

¿Qué es la inyección SQL y cómo prevenirla en C#?

Tim Corey
34m 33s

La inyección SQL es una técnica de inyección de código que permite a los atacantes enviar código SQL malicioso a su servidor de base de datos a través de la entrada del usuario. En su vídeo "What Is SQL Injection And How Do I Prevent It in C#?", Tim Corey demuestra exactamente cómo aparecen las vulnerabilidades de inyección SQL en el código real, muestra varios ejemplos de ataques de inyección SQL con éxito (incluidos ataques destructivos y basados en uniones) y recorre técnicas prácticas de prevención de inyección SQL que puede aplicar en C#. Este artículo sigue el recorrido de Tim para que puedas ver los problemas exactos y las soluciones que muestra.

Aplicación demo y por qué es importante

Tim comienza con una pequeña aplicación de demostración WPF vinculada a una InjectableDB local (tablas People y Secrets). El cuadro de búsqueda de la aplicación, similar a un formulario web, toma los datos introducidos por el usuario (un apellido) y construye una consulta SQL para devolver el ID, el nombre y el apellido. "Funciona" -escriba Corey y obtendrá Tim Corey-, pero Tim hace hincapié en el punto central: "Que una aplicación funcione no significa que sea segura." Una aplicación web que funcione puede seguir teniendo vulnerabilidades de inyección SQL cuando la entrada suministrada por el usuario se inserta directamente en sentencias SQL mediante concatenación de cadenas o SQL dinámico.

El código inseguro - concatenación de cadenas y SQL dinámico

Tim muestra el patrón exacto de inseguridad que utilizan muchos desarrolladores:

var sql = $"SELECT * FROM People WHERE LastName = '{searchText}'";
var results = connection.Query<Person>(sql);
var sql = $"SELECT * FROM People WHERE LastName = '{searchText}'";
var results = connection.Query<Person>(sql);

Esta consulta original utiliza la concatenación de cadenas para crear una sentencia SQL. Tim advierte: si ves código que inyecta entradas de usuario directamente en consultas SQL, detente: se trata de una vulnerabilidad de inyección SQL. Los atacantes pueden crear entradas maliciosas que cambien la estructura de sus comandos SQL o incluso ejecutar sentencias SQL maliciosas adicionales.

Cómo lo explota un atacante - UNION y DROP

Para mostrar cómo funciona un ataque de inyección SQL, Tim reproduce consultas en SQL Server y luego crea inyecciones utilizando UNION ALL y comentarios SQL (--) para ocultar los caracteres finales. Ejemplos de cargas maliciosas que Tim demuestra:

  • Inyección SQL basada en la unión para leer otras tablas:

    UNION ALL SELECT ID, Nombre de usuario COMO Nombre, Contraseña COMO Apellido FROM Secrets;

    Esto mezcla los resultados de Secrets en el conjunto de resultados SELECT original, exponiendo datos sensibles como nombres de usuario y contraseñas.

  • Inyección destructiva para eliminar tablas:

    TABLA DE DEMOSTRACIÓN DE DROP TABLE;

    Esto ejecuta una segunda sentencia SQL (DROP TABLE) terminando la primera sentencia con un punto y coma y luego añadiendo el comando destructivo. Tim muestra que la tabla desaparece: la base de datos ha sido modificada por SQL malicioso.

El punto de Tim: los atacantes no necesitan conocer los nombres de tablas o columnas de antemano - pueden enumerar los nombres de tablas o columnas de los servidores de bases de datos, o simplemente probar técnicas ciegas o basadas en el tiempo para descubrir el comportamiento.

Corrección 1 - Consultas parametrizadas

La primera y principal defensa de Tim es dejar de construir cadenas SQL con datos de usuario. Sustituir SQL dinámico por consultas parametrizadas:

string sql = "SELECT * FROM People WHERE LastName = @LastName";
var results = connection.Query<Person>(sql, new { LastName = searchText });
string sql = "SELECT * FROM People WHERE LastName = @LastName";
var results = connection.Query<Person>(sql, new { LastName = searchText });

Tim explica que la parametrización (uso al estilo de las sentencias preparadas) significa que la base de datos trata la entrada suministrada por el usuario estrictamente como datos: el SQL malicioso se convierte en un simple valor de cadena y no puede cambiar la estructura SQL. Esto evita muchos ataques de inyección SQL comunes, incluidas las cargas útiles basadas en uniones y las cargas útiles ; Comandos DROP TABLE.

También recomienda emparejar la parametrización con una validación de entrada mínima: desinfectar o bloquear caracteres poco probables en un apellido (por ejemplo, punto y coma o marcadores de comentario --) mientras se permiten caracteres legítimos como apóstrofes (O'Reilly). Las consultas parametrizadas + la limpieza de entradas ofrecen una protección sustancial contra los ataques de inyección SQL.

Corrección 2 - Procedimientos almacenados

A continuación, Tim muestra dos procedimientos almacenados: un procedimiento almacenado inseguro que concatena SQL dentro del proc y luego lo ejecuta, y un procedimiento almacenado seguro que utiliza parámetros directamente.

  • El procedimiento almacenado inseguro construye una cadena @sql a partir del parámetro y la ejecuta - aún vulnerable a la inyección.

  • El procedimiento almacenado seguro realiza SELECT ... WHERE LastName = @LastName y se ejecuta con el parámetro - safe.

Tim aclara: los procedimientos almacenados no son una cura automática si todavía se construye SQL dinámico dentro de ellos. Pero cuando se utilizan correctamente (sin SQL dinámico), los procedimientos almacenados ayudan a centralizar las sentencias SQL, facilitando la parametrización y la auditoría de las consultas. Los procedimientos almacenados también pueden ayudar a simplificar la prevención de inyecciones SQL en tu aplicación.

No te fíes de ningún dato, ni siquiera de los datos de las bases de datos

Un punto importante y a menudo olvidado que plantea Tim: no hay que confiar ciegamente en los datos recuperados de la propia base de datos SQL. En ocasiones, los atacantes colocan cargas útiles maliciosas en columnas (una "bomba de relojería") que posteriormente serán concatenadas en SQL dinámico por otro proceso. Tim insiste: utiliza siempre parámetros y desinfecta los datos en cada paso -ya procedan de un formulario web, de la carga de un archivo o de tu propia base de datos- para que la entrada maliciosa no pueda convertirse más tarde en una vía de inyección.

Consejo de bonificación - privilegio mínimo y limitación de privilegios de base de datos

Más allá de las correcciones de código, Tim recomienda una configuración defensiva: limitar los privilegios de base de datos para las cuentas de su aplicación. En su demo, la conexión utiliza una cuenta de administrador a través de la seguridad integrada - peligrosa. En su lugar, utilice el principio del menor privilegio:

  • Crea una cuenta de base de datos para la aplicación con solo los derechos que necesite.

  • Si utiliza procedimientos almacenados, conceda a esa cuenta únicamente el permiso EJECUTAR en procedimientos almacenados específicos y nada más.

  • No otorgue a las cuentas de la aplicación amplios privilegios de administrador que permitan DROP TABLE, listar todas las tablas o leer otras bases de datos.

Esto reduce el impacto de un ataque de inyección SQL exitoso: aunque la inyección sea posible, el atacante no puede hacer más de lo que la cuenta tiene permitido.

Tim también señala que Entity Framework complica esta tarea: EF a menudo requiere permisos elevados (migraciones, cambios de esquema). Si utilizas EF en producción, planifica cuidadosamente sus permisos y despliegue.

Recap - detener, parametrizar, sanear, limitar

Tim concluye su vídeo con una lista de comprobación clara para evitar la inyección SQL en aplicaciones C#:

  1. Deje de crear sentencias SQL con concatenación de cadenas o SQL dinámico que incluya entradas del usuario.

  2. Utilice consultas parametrizadas / patrones de sentencias preparadas para que los datos del usuario se traten siempre como datos.

  3. Sanear la entrada cuando sea necesario (punto y coma, comentarios SQL, caracteres inesperados).

  4. Prefiera procedimientos almacenados seguros (sin SQL dinámico en su interior) para centralizar la lógica de consulta.

  5. Aplicar privilegios mínimos a las cuentas de base de datos: limitar lo que puede hacer el usuario de la base de datos de la aplicación.

  6. Revise el código (especialmente los lugares que construyen SQL dinámicamente) y compruebe si hay fallos de inyección SQL.

Última advertencia de Tim: una gestión descuidada de las entradas de los usuarios, el SQL dinámico y los privilegios excesivos de las bases de datos pueden dar lugar a infracciones graves: filtración de datos confidenciales, destrucción de tablas o exfiltraciones no detectadas durante mucho tiempo. Trate la prevención de inyecciones SQL como un requisito de seguridad básico, no como un pulido opcional.

Hero Worlddot related to ¿Qué es la inyección SQL y cómo prevenirla en C#?
Hero Affiliate related to ¿Qué es la inyección SQL y cómo prevenirla en C#?

Gana más compartiendo lo que te gusta

¿Creas contenidos para desarrolladores que trabajan con .NET, C#, Java, Python o Node.js? ¡Convierte tu experiencia en un ingreso extra!

Equipo de soporte de Iron

Estamos disponibles online las 24 horas, 5 días a la semana.
Chat
Email
Llámame