Scenario - in the application we are creating SQL statement dynamically.
string query = ”Select {0} from TABLENAME where {1}”;
string.Format(query, columnNames, whereClause);
These variable column names are coming from a third party service from our own domain. However, while running the fortify scan for this code, I am getting an error for SQL injection. The message says that the data columnNames is coming from the UNTRUSTED service. How do I handle this scenario?
Related
I am trying to build a website vulnerable to SQL injections (for educational purposes) with an ASP.NET API. To do that, I would like to call FromSql with a previously prepared SQL query as such:
String query = "SELECT * FROM users WHERE email = '"+email
+"' AND password = '"+password+"'";
return RepositoryContext.Users.FromSql(query).FirstOrDefault();
But this code will not compile as FromSql is expecting a FormattableString, and not a String. I get the following error message:
Error CS1503 Argument 2: cannot convert from 'string' to 'System.FormattableString'
The following would compile, but then this code would not be vulnerable to SQL injections anymore as Entity Framework will perform a prepared SQL query and convert email and password to SQL query parameters :
return RepositoryContext.Users.FromSql(
$"SELECT * FROM users WHERE email = {email}
AND password = {password}").FirstOrDefault();
Is there a way to convert a String to a FormattableString?
Thank you for your help.
What you see is Entity Framework doing a good job at preventing SQL injection by default.
You can teach SQL injection by using the FromSqlRaw method instead which takes a string that could be maliciously crafted.
See also Entity Framework Core SQL Queries → Passing parameters documentation:
Pay close attention to parameterization when using SQL queries
When introducing any user-provided values into a SQL query, care must be taken to avoid SQL injection attacks. SQL injection occurs when a program integrates a user-provided string value into a SQL query, and the user-provided value is crafted to terminate the string and perform another malicious SQL operation. To learn more about SQL injection, see this page.
The FromSql and FromSqlInterpolated methods are safe against SQL injection, and always integrate parameter data as a separate SQL parameter. However, the FromSqlRaw method can be vulnerable to SQL injection attacks, if improperly used. See below for more details.
It is changing my queries and appears to be fully qualifying my tables without me explicitly telling it to. Is there a way to stop it from doing that?
Here is the pertinent information as I see it. Let me know if anything else would be helpful.
We had a SQL Server named serverName. It's been in production for years. It was migrated away from a Windows 2008 Server to a Windows 2012 Server. The new server's name is sql_1234_4321 (not the real name but as terrible)
We have nth number of applications that were hitting the old serverName SQL Server so we took the old server offline and created a DNS entry for serverName that points at the new sql_1234_4321 hoping we wouldn't have to hit the connection strings for all the apps that were hitting the old server.
This worked for the most part except for some C# ASP.NET MVC apps.
They are using System.Data.SqlClient.SqlCommand.
Connection string:
Data Source=serverName;Initial Catalog=USData; Persist Security Info=True; User ID=appUn;Password=appPw
SQL query:
select FirstName from Customers
Code:
using (SqlCommand cmd = new SqlCommand(query, sqlConnection))
{
if (parameters != null)
{
cmd.Parameters.AddRange(parameters.ToArray());
}
var reader = cmd.ExecuteReader();
var results = new List<TType>();
while (reader.Read())
{
results.Add(convert(reader));
}
return results;
}
I get an error:
Could not find server 'serverName' in sys.servers.Verify that the correct server name was specified. If necessary, execute the stored procedure sp_addlinkedserver to add the server to sys.servers.
Why this error? The only time serverName is referenced is in the connection string. My query should just use default namespaces once its on the server. But it appears that my query is being fully qualified at some point in the process as the following:
select FirstName from serverName.USData.dbo.Customers
I added a linked server serverName on the new sql_1234_4321 server that just points back to itself and this seemed to fix the problem. However, this feels absolutely dirty and makes me wonder if it REALLY is doing a cross server query at that point or if its smart enough to say "HEY! we are hitting ourself so don't worry about going out to the network and making this more expensive than it should be" but i doubt it.
I thought about using synonyms but the problem is we have tables with the server name in them. And there may be queries hitting the server with the server name in them so the following would not work:
CREATE SYNONYM serverName FOR sql_1234_4321;
So then it would make sense that I'd have to make a specific synonym for each database on the server:
CREATE SYNONYM serverName.database1 FOR sql_1234_4321.database1;
CREATE SYNONYM serverName.database2 FOR sql_1234_4321.database2;
CREATE SYNONYM serverName.database3 FOR sql_1234_4321.database3;
CREATE SYNONYM serverName.database4 FOR sql_1234_4321.database4;
CREATE SYNONYM serverName.database5 FOR sql_1234_4321.database5;
CREATE SYNONYM serverName.database6 FOR sql_1234_4321.database6;
CREATE SYNONYM serverName.database7 FOR sql_1234_4321.database7;
CREATE SYNONYM serverName.database8 FOR sql_1234_4321.database8;
CREATE SYNONYM serverName.database9 FOR sql_1234_4321.database9;
CREATE SYNONYM serverName.database10 FOR sql_1234_4321.database10;
As you can see, this would be a nightmare to maintain and besides that feels super dirty.
My question is this... At what point is the table name being fully qualified out based on the connection string? Is there a way to prevent that from happening?
David Browne led me to find the issue. My query actually had a view referenced and the view had the reference to the old server. Huge oversight on my part to not notice that. Thanks David
I have a SQL CLR stored procedure written in c# (.NET4). Its purpose is to allow a trigger on a table in a SQL Server 2012 database to call a web service which then processes the data in that table.
However, there are several different databases which will all have triggers using this assembly. My web service needs to know which database is triggered the call to it in order to know where to get the data from.
I could simply add a parameter to my stored procedure but I want to keep things simple from the database side. Is there any way, in .NET, to obtain information about the database to which the assembly is attached?
Aah, found one:
This still opens a context connection to the database, but it's about the only way I can see.
using (SqlConnection conn = new SqlConnection("context connection=true"))
{
conn.Open();
string dbName = conn.Database
}
That's from an MSDN article. Also, the MSDN article on Context Connection.
I am about to deploy my application and have came into a bit of trouble.
I have the connection string for the database held in the application.settings and need a way to check if the database exists when the program first starts up, and if it doesn't, i need the program to create it before starting the program.
I am assuming it would be a mysql statement to check if db exists, if not create. However, I don't know where or how to do this, can I create a mysql dump of a blank database with tables etc already created and use that?
I have already stored the mysql dll files locally so there is no problem with that, its just creating the database that the string wants to connect to before the application runs so there are no connection errors straight away.
Thanks.
You can do this by running the following SQL statement:
SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = "my_db"
If it doesn't exist from the result set you get returned you can then create it.
This does pose questions regarding MySQL permissions and if your application should have user rights that enable such checking.
Edit in response of comments.
It isn’t clear if you create the connection string or not – I’ll assume the worst and that it is a part of the setup so your client can enter it (if you do know it the process below simplifies.
I would pass the connection string to the constructor of the MySqlConnectionStringBuilder class, this then makes it easy to connect to the database using the MySqlConnection class. I would use the properties from the new instance of the MySqlConnectionStringBuilder class (Server, Host, User etc) to setup the MySqlConnection class.
If the connection didn’t work I would return information to the user and they can update their connection string.
Once I’ve successfully connected to the database I would then use the database name from the Database property of my MySqlConnectionStringBuilder instance to build the query above.
If the command returns NULL the database doesn't exist and then needs creating, if the database does exist then the command will return the name of the database.
Now there are two paths:
It Doesn't exist – It needs creating, I would probably have an external SQL file with the create statements in (can be produced by MySQL dump by using the –nodata option). I would parse this file and execute the create statements
It does exist – I would now check the structure of the database to make sure it is compatible before continuing the installation.
I am trying to put up a code to create a databases from my C# code (asp.net website).
This is my code:
SqlCommand myCommand = new SqlCommand("CREATE DATABASE #dbname", nn);
myCommand.Parameters.Add("dbname", dbname);
myCommand.ExecuteNonQuery();
nn.Close();
well, its not working. its giving me an error:
incorrect syntax near '#dbname'
BUT. if I won't use parameters, people can SQL inj to my database. do you have any idea how can use anything, to get the database name from a textbox. and that people can't SQL inj me database?
You can't use parameters in CREATE DATABASE or other DDL commands.
I'd suggest using SQL Server Management Objects instead of SQL