question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

String interpolation in 2.0 is a breaking change when injecting parameters in invalid places

See original GitHub issue

In EF Core 1.x the following worked as expected:

var databaseName = "Customer";
this.Database.ExecuteSqlCommand($"ALTER DATABASE [{databaseName}] SET CHANGE_TRACKING = ON (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON)");

In EF Core 2.0 the new string interpolation feature extracts the “databaseName” and injects it as a parameter, but SQL Server doesn’t allow the database name to be parameterized so it throws an exception:

Exception message: User does not have permission to alter database '@p0', the database does not exist, or the database is not in a state that allows access checks.
ALTER DATABASE statement failed.
Stack trace:
System.Data.SqlClient.SqlException occurred
  HResult=0x80131904
  Message=User does not have permission to alter database '@p0', the database does not exist, or the database is not in a state that allows access checks.
ALTER DATABASE statement failed.
  Source=Core .Net SqlClient Data Provider
  StackTrace:
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite, String method)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite, String methodName)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary`2 parameterValues)
   at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.ExecuteSqlCommand(DatabaseFacade databaseFacade, RawSqlString sql, IEnumerable`1 parameters)
   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.ExecuteSqlCommand(DatabaseFacade databaseFacade, FormattableString sql)
   at Contoso.Web.Services.SqlDatabaseEventRepository.CustomerDataContext.Initialize() in C:\Code\Contoso.Web\Services\SqlDatabaseCustomerRepository\CustomerDataContext.cs:line 33
   at Contoso.Web.Services.SqlDatabaseEventRepository.SqlDatabaseCustomerRepository.Initialize() in C:\Code\Contoso.Web\Services\SqlDatabaseCustomerRepository\SqlDatabaseCustomerRepository.cs:line 25
   at Contoso.Web.ApplicationInitializer..ctor() in C:\Code\Contoso.Web\ApplicationInitializer.cs:line 13

Further technical details

EF Core version: Microsoft.EntityFrameworkCore.SqlServer version 2.0.0 Database Provider: Microsoft.EntityFrameworkCore.SqlServer Operating system: Windows 10 version 1703 IDE: Visual Studio 2017.3

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

8reactions
NickCravercommented, Sep 9, 2017

@smitpatel It’s not all all obvious to a user that this escapes:

this.Database.ExecuteSqlCommand($"....{..}...");

but this does not:

var sql = $"....{..}...";
this.Database.ExecuteSqlCommand(sql);

There’s a lot of information telling people that this is safe now, but the actual overload and resolution rules make it very brittle and there are even a lot of refactorings (e.g. do this replacement once) that unwittingly break it, even introducing SQL injection rather silently. It’s much better, IMO, to tell users to explicitly parameterize.

We’ve very explicitly stayed away from doing this with Dapper because the messaging is so dangerous, and we see a lot more raw SQL than any other library. I posted a repo (that you can run) showing just some of the injection cases and some of the many ways users can easily screw this up: https://github.com/NickCraver/EFCoreInjectionSample/blob/master/Program.cs

0reactions
jnm2commented, Sep 9, 2017

If you provide only a FormattableString method and not a string overload, problem solved, right?

The method that takes a raw string could have the suffix Raw or Unsafe.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Can you use a custom string interpolation character in Scala?
Is it possible to use a different interpolation character to avoid the double escape? val characterA = "Randolph" val characterB = "Mortimer" ...
Read more >
String Interpolation in Java
String interpolation is a straightforward and precise way to inject variable values into a string. It allows users to embed variable references ...
Read more >
String Interpolation in C# 10 and .NET 6
This “string interpolation” functionality enables developers to place a $ character just before the string; then, rather than specifying  ...
Read more >
Passing parameters to SQL queries
When parameters are used, in order to include a literal % in the query you can use the %% string: cur.execute("SELECT (%s %...
Read more >
Preventing SQL Injection Attacks With Python
In this step-by-step tutorial, you'll learn how you can prevent Python SQL injection. You'll learn how to compose SQL queries with parameters, as...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found