flawopen.com/SQL Injection в C#/EF Core FromSqlRaw
Безопасен ли FromSqlRaw в Entity Framework Core?
Да, если значения передаются отдельно от SQL-текста. FromSqlRaw принимает SQL напрямую: конкатенация или интерполяция недоверенных данных в строку может изменить запрос. Передавайте значения отдельными аргументами параметров либо передавайте интерполированный FormattableString в FromSqlInterpolated (или в FromSql начиная с EF Core 7). Параметр — это запечатанный слот для значения: база получает значение, а не новые инструкции SQL.
// Сначала интерполяция вычисляется в обычную строку. var sql = $"SELECT * FROM Users WHERE Id = {id}"; // FromSqlRaw получает SQL-текст, а не FormattableString. var query = context.Users.FromSqlRaw(sql);
// EF Core 3–6: позиционный placeholder и отдельное значение. var byRawParameter = context.Users.FromSqlRaw( "SELECT * FROM Users WHERE Id = {0}", id); // EF Core 3+: передайте интерполяцию в API с параметризацией. var byInterpolation = context.Users.FromSqlInterpolated( $"SELECT * FROM Users WHERE Id = {id}"); // EF Core 7+: FromSql также принимает FormattableString. var byFromSql = context.Users.FromSql( $"SELECT * FROM Users WHERE Id = {id}");
Неочевидный нюанс
FromSqlInterpolated принимает FormattableString. EF Core читает формат и аргументы и передаёт каждое интерполированное значение в базу как отдельный параметр; перехвата на этапе компиляции нет. В EF Core 7 появился FromSql с тем же безопасным поведением для интерполированных строк. В предыдущих версиях используйте FromSqlInterpolated. FromSqlRaw также безопасен, если placeholder, например {0}, соответствует значению, переданному отдельным аргументом. Если сначала сохранить интерполированную строку в string, метаданные параметров уже потеряны.
Параметры связывают значения, а не имена столбцов
Параметр базы данных задаёт значение, но не имя таблицы или столбца, оператор SQL или направление сортировки. Если структура запроса должна меняться, выбирайте каждый идентификатор из фиксированного списка разрешённых значений в коде приложения. Не пытайтесь параметризовать идентификаторы и не заменяйте список фильтрацией символов. Также проверьте права доступа и стоимость запроса: допустимый столбец всё ещё может раскрыть чувствительные данные или вызвать дорогое сканирование.
- ✓Для значений используйте FromSqlInterpolated/FromSql с прямой интерполяцией либо FromSqlRaw с отдельными параметрами.
- ✓Не передавайте склеенные или интерполированные пользовательские данные в FromSqlRaw. Не преобразуйте сначала FormattableString в string.
- ✓Динамические идентификаторы выбирайте только из фиксированного списка разрешённых значений. Отдельно проверяйте права доступа к полю и наличие эффективного индекса.
- ✓Предпочитайте LINQ, если с его помощью запрос выражается понятно: параметризация предотвращает SQL-инъекцию в синтаксис, но не обеспечивает проверку прав и бизнес-правил.
Как проверить кодовую базу
rg -n -g '*.cs' '(FromSqlRaw|FromSqlInterpolated|FromSql)\s*\(' .Частые вопросы
Да, если в SQL есть placeholders, а значения передаются отдельными аргументами, например FromSqlRaw("... {0}", value). Не добавляйте значение в SQL-строку конкатенацией или интерполяцией заранее.
Он принимает FormattableString, который хранит формат и значения отдельно. EF Core преобразует каждое значение в параметр базы данных. Если сначала присвоить результат интерполяции переменной string или передать его в FromSqlRaw, защита теряется.
Нет. Параметры представляют только значения. Для динамических идентификаторов сопоставляйте выбор пользователя с фиксированным списком допустимых имён и проверяйте права доступа и стоимость запроса.
Источники
- Microsoft Learn — SQL-запросы и параметры EF Core
- Microsoft Learn — API FromSqlRaw
- Microsoft Learn — API FromSqlInterpolated
- OWASP — рекомендации по предотвращению SQL Injection
- FlawOpen — SQL Injection в C#