FlawOpen

flawopen.com/SQL Injection en C#/EF Core FromSqlRaw

¿Es seguro FromSqlRaw de Entity Framework Core?

Guía de referencia
Respuesta breve

Sí, si los valores permanecen separados del texto SQL. FromSqlRaw acepta SQL sin procesar; concatenar o interpolar entradas no confiables en su cadena puede alterar la consulta. Usa argumentos de parámetros separados o pasa un FormattableString interpolado a FromSqlInterpolated (o a FromSql en EF Core 7+). Piensa en un parámetro como una ranura sellada: la base de datos recibe el valor, no nuevas instrucciones SQL.

INSEGURO — la interpolación se convierte en una cadena simple
// La interpolación se evalúa primero como una cadena.
var sql = $"SELECT * FROM Users WHERE Id = {id}";

// FromSqlRaw recibe texto SQL, no un FormattableString.
var query = context.Users.FromSqlRaw(sql);
SEGURO — los valores siguen siendo parámetros
// EF Core 3–6: marcador posicional y valor separado.
var byRawParameter = context.Users.FromSqlRaw(
    "SELECT * FROM Users WHERE Id = {0}", id);

// EF Core 3+: pasa la interpolación a la API parametrizada.
var byInterpolation = context.Users.FromSqlInterpolated(
    $"SELECT * FROM Users WHERE Id = {id}");

// EF Core 7+: FromSql también recibe FormattableString.
var byFromSql = context.Users.FromSql(
    $"SELECT * FROM Users WHERE Id = {id}");

El detalle contraintuitivo

FromSqlInterpolated recibe un FormattableString. EF Core lee el formato y los argumentos y envía cada valor interpolado como un parámetro independiente de la base de datos; no hay interceptación en tiempo de compilación. EF Core 7 añadió FromSql con el mismo comportamiento seguro para cadenas interpoladas. Las versiones anteriores usan FromSqlInterpolated. FromSqlRaw también es seguro si marcadores como {0} corresponden a valores pasados como argumentos separados. Una cadena interpolada guardada primero como string ya perdió sus metadatos de parámetros.

Los parámetros vinculan valores, no nombres de columnas

Un parámetro de base de datos representa un valor, no el nombre de una tabla o columna, un operador SQL ni una dirección de ordenación. Si la estructura de la consulta debe variar, elige cada identificador de una lista permitida fija en el código de la aplicación; no intentes parametrizar identificadores ni sustituyas la lista por filtros de caracteres. Revisa también la autorización y el coste: una columna válida puede exponer datos sensibles o provocar consultas costosas.

Cómo revisar tu código

rg -n -g '*.cs' '(FromSqlRaw|FromSqlInterpolated|FromSql)\s*\(' .
En cada llamada, revisa la sobrecarga y el tipo del argumento. Rastrea datos no confiables para detectar concatenación, interpolación pasada a FromSqlRaw o un FormattableString convertido en string; analiza los identificadores dinámicos por separado.

Preguntas frecuentes

¿FromSqlRaw es seguro si recibe parámetros?

Sí, cuando el SQL contiene marcadores y los valores se pasan como argumentos separados, por ejemplo FromSqlRaw("... {0}", value). No concatenes ni interpoles primero el valor en la cadena SQL.

¿Por qué FromSqlInterpolated es seguro con una cadena interpolada de C#?

Recibe un FormattableString, que conserva por separado el formato y los valores. EF Core convierte cada valor en un parámetro de base de datos. Si asignas primero la interpolación a un string o la pasas a FromSqlRaw, se pierde esa protección.

¿Pueden los parámetros reemplazar nombres de tablas o columnas?

No. Los parámetros solo representan valores. Para identificadores dinámicos, asigna las opciones del usuario a una lista permitida fija de identificadores conocidos y revisa la autorización y el coste de la consulta.

Referencias

Véase también Inyección SQL (CWE-89): cómo funciona y cómo evitarla en cada lenguaje — flawopen.com FastAPI y SQL Injection: qué protege Pydantic GORM Raw SQL: evita la inyección SQL en Go ¿Basta escapar comillas para evitar SQL Injection? ¿Basta con usar un ORM para prevenir la inyección SQL? ¿Knex.js es seguro frente a SQL Injection? MyBatis ${} vs #{}: evitar SQL Injection