flawopen.com/SQL Injection en C#/EF Core FromSqlRaw
¿Es seguro FromSqlRaw de Entity Framework Core?
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.
// 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);
// 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.
- ✓Para valores, usa FromSqlInterpolated/FromSql con interpolación directa, o FromSqlRaw con parámetros separados.
- ✓Nunca pases datos concatenados o interpolados del usuario a FromSqlRaw. No conviertas primero un FormattableString en string.
- ✓Para identificadores dinámicos, elige solo de una lista permitida fija. Comprueba por separado los permisos y que el campo tenga un índice eficiente.
- ✓Prefiere LINQ cuando exprese la consulta con claridad; la parametrización evita la inyección de sintaxis SQL, pero no aplica autorización ni reglas de negocio.
Cómo revisar tu código
rg -n -g '*.cs' '(FromSqlRaw|FromSqlInterpolated|FromSql)\s*\(' .Preguntas frecuentes
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.
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.
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
- Microsoft Learn — consultas SQL y parámetros de EF Core
- Microsoft Learn — API FromSqlRaw
- Microsoft Learn — API FromSqlInterpolated
- OWASP — guía de prevención de SQL Injection
- FlawOpen — SQL Injection en C#