Currently, I am creating an SQL Query by doing something like
string SQLQuery = "SELECT * FROM table WHERE ";
foreach(word in allTheseWords)
{
SQLQuery = SQLQuery + " column1 = '" + word + "' AND";
}
I understand that this can lead to an SQL Injection attack. I don't know how to pass an array as a parameter
where report in #allTheseWords
===========
I am using SQL Server 2012
Unfortunately, you cannot pass an array as a parameter without adding a user-defined type for table-valued parameters. The simplest way around this restriction is to create individually named parameters for each element of the array in a loop, and then bind the values to each of these elements:
string SQLQuery = "SELECT * FROM table WHERE column1 in (";
for(int i = 0 ; i != words.Count ; i++) {
if (i != 0) SQLQuery += ",";
SQLQuery += "#word"+i;
}
...
for(int i = 0 ; i != words.Count ; i++) {
command.Parameters.Add("#word"+i, DbType.String).Value = words[i];
}
You can also create a temporary table, insert individual words in it, and then do a query that inner-joins with the temp table of words.
Here is the recommendation from Microsoft:
Use Code Analysis to detect areas in your Visual Studio projects that are prone to sql injection;
Refer to the article on how to reduce risk of attack:
On short they talk about:
using a stored procedure.
using a parameterized command string.
validating the user input for both type and content before you build the command string.
Btw, you can enable static analysis as part of your build process and configure it so that when a security rule is broken, the build also breaks. Great way to make sure your team writes secure code!
Using ADO you can do it with the help of params
SqlConnection Con = new SqlConnection(conString);
SqlCommand Com = new SqlCommand();
string SQLQuery = "SELECT * FROM table WHERE ";
int i=1;
foreach(word in words)
{
Com.Parameters.Add("#word"+i.ToString(),SqlDbType.Text).Value = word;
SQLQuery = SQLQuery + " column1 = '#word"+i.ToString()+"' AND ";
i++;
}
Com.CommandText =SQLQuery;
For SQL Server, you'd use a Table-Valued Parameter. SQL has one structure that represents a collection of multiple items of the same type. It's called a table. It doesn't have arrays.
Of course, your supposed updated query:
where report in #allTheseWords
Isn't equivalent to your original query, but may be closer to the intent. In the query constructed using AND, you're saying that the same column, in the same row has to be equal to multiple different words. Unless all of the words are equal, this will never return any rows. The updated query answers whether any of the words match, rather than all.
You need to use prepared statements. The way those are handled is that you write your query and put placeholders for the values you want to use. Here's an example:
SELECT * FROM table WHERE column1 = #word
You then have to go through a prepare phase where the SQL engine knows it will need to bind parameters to the query. You can then execute the query. The SQL engine should know when and how to interpret the parameters you bind to your query.
Here's some code to do that:
SqlCommand command = new SqlCommand(null, rConn);
// Create and prepare an SQL statement.
command.CommandText = "SELECT * FROM table WHERE column1 = #word";
command.Parameters.Add ("#word", word);
command.Prepare();
command.ExecuteNonQuery();
I combine the use of params with HtmlEncoding(to get rid of special characters where not needed). Give that a shot.
using (SqlConnection conn = new SqlConnection(conString))
{
string sql = "SELECT * FROM table WHERE id = #id";
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.paramaters.AddWithValue("#id", System.Net.WebUtility.HtmlEncode(id));
conn.Open();
using (SqlDataReader rdr = cmd.ExecuteReader())
{
}
}
}
Related
I am trying to modify a table data using a SQL statement
foreach (Words words in Words_DB.Records)
{
string _IPAUS = words.IPAUS;
string _IPAUK = words.IPAUK;
query = "UPDATE Words SET IPAUK='" + _IPAUK + "',IPAUS='" + _IPAUS + "' WHERE WORD='" + words.Word + "'";
command.Parameters.Clear();
command.CommandText = query;
//command.Parameters.AddWithValue("#IPAUK", _IPAUK);
//command.Parameters.AddWithValue("#IPAUS", _IPAUS);
//command.Parameters.AddWithValue("#WORD", words.Word);
int a = command.ExecuteNonQuery();
}
A example of query is UPDATE Words SET IPAUK='ɑːd.vɑːk',IPAUS='ɑːrd.vɑːrk' WHERE WORD='aardvark'
The problem is when a read the database data I receive :
But, when I use the MySql Tools to execute the Query the result is right.
What I am doing wrong?
Regards
The question concatenates raw input to generate a SQL query which exposes to SQL injection and bugs like this one. If _IPAUK contained '; -- all the data in that column would be lost.
In this case it seems the code is trying to pass Unicode data using ASCII syntax, resulting in mangled data.
The solution to both SQL injection and conversion issues is to use parameterized queries. In a parameterized query, the actual parameter values never become part of the query itself. The server compiles the SQL query into an execution plan and executes that using the parameter values.
await using var connection = new MySqlConnection(connString);
await connection.OpenAsync();
// Insert some data
using (var cmd = new MySqlCommand())
{
cmd.Connection = connection;
cmd.CommandText = "UPDATE Words SET IPAUK=#IPAUK,IPAUS=#IPAUS WHERE WORD=#Word";
cmd.Parameters.AddWithValue("IPAUK", words.IPAUK);
cmd.Parameters.AddWithValue("IPAUS", words.IPAUS);
cmd.Parameters.AddWithValue("Word", words.Word);
await cmd.ExecuteNonQueryAsync();
}
The example uses the open source MySQLConnector ADO.NET Driver instead of Oracle's somewhat ... buggy driver.
The code can be simplified even more by using Dapper to construct the command, parameters and handle the connection automagically. Assuming words only has the IPAUK, IPAUS and Word properties, the code can be reduced to three lines :
var sql="UPDATE Words SET IPAUK=#IPAUK,IPAUS=#IPAUS WHERE WORD=#Word";
await using var connection = new MySqlConnection(connString);
await connection.ExecuteAsync(sql,words);
Dapper will construct a MySqlCommand, add parameters based on the properties of the parameter object (words), open the connection, execute the command and then close the connection
Thanks a lot for your helps.
This is my final code working properly.
string query = "UPDATE Words SET IPAUK=#IPAUK,IPAUS=#IPAUS WHERE WORD=#WORD";
var command = DatabaseConnection.MySql_Connection.CreateCommand();
try
{
foreach (Words words in Words_DB.Records)
{
MySqlParameter IPAUSp = new MySqlParameter("#IPAUS", MySqlDbType.VarChar, 60);
MySqlParameter IPAUKp = new MySqlParameter("#IPAUK", MySqlDbType.VarChar, 60);
MySqlParameter WORD = new MySqlParameter("#WORD", MySqlDbType.VarChar, 50);
command.Parameters.Clear();
command.CommandText = query;
command.Parameters.AddWithValue(IPAUKp.ToString(), words.IPAUK);
command.Parameters.AddWithValue(IPAUSp.ToString(), words.IPAUS);
command.Parameters.AddWithValue(WORD.ToString(), words.Word);
int a = command.ExecuteNonQuery();
}
}
Try it like this:
command.CommandText = "UPDATE Words SET IPAUK= #IPAUK, IPAUS= #IPAUS WHERE WORD= #Word;";
// Match these to the column type and length in the DB
command.Parameters.Add("#IPAUK", MySQlDbType.VarChar, 30);
command.Parameters.Add("#IPAUS", MySQlDbType.VarChar, 30);
command.Parameters.Add("#Word", MySQlDbType.VarChar, 30);
foreach (Words words in Words_DB.Records)
{
command.Parameters["#IPAUK"].Value = words.IPAUK;
command.Parameters["#IPAUS"].Value = words.IPAUS;
command.Parameters["#Word"].Value = words.Word;
command.ExecuteNonQuery();
}
Notice how the above minimizes the work done in the loop, which should improve performance, while also fixing the HUGE GAPING SECURITY ISSUE in the question from using string concatenation to build the query.
Separately, I have the impression Words_DB.Records is the result of a prior query. It's highly likely you could eliminate this entire section completely by updating the prior query to also do the update in one operation on the server. Not only would that greatly reduce your code, it will likely improve performance here by multiple orders of magnitude.
I would like to do the same thing as oracle sqldeveloper do with parametered queries (but from c#, not java).
Let's say there's an arbitrary, user supplied query, eg
select * from dual where 1 = :parameter
My task is to parse safely similar strings, identify the parameters, ask them from user and execute the query.
Which is the right / safe approach? I guess, there's some oracle client api to do this. Or is the right way using some pl/sql stuff (eg. from DBMS_SQL)? I couldn't find such a thing yet...
Update / clarification: see the example code below:
// user enters the query string with parameters somehow:
string sql = AskUserForSelectString();
// now the value of sql is:
// "select column0 from tablename where column1 = :param1 and column2 = :param2 ;"
// this is my original question: HOW TO DO THIS?
List<string> param_names = OracleParseQueryAndGiveMyParameters(sql);
// param_names is now a list of ":param1",":param2"
// ask user again for parameter values:
var param_values = new List<string>();
foreach (var param_name in param_names)
{
string param_value = AskUserForParameterValue(param_name);
param_values.Add(param_value);
}
// give the parameter values for the query in safe way:
using (var cmd = new SqlCommand(sql, myDbConnection))
{
for (int i=0; i< param_names.Count; i++)
cmd.Parameters.AddWithValue(param_names[i], param_values[i]);
var result = cmd.ExecuteReader();
// process result...
}
The key point is that I don't know the parameters in advance. This is exactly what SqlDeveloper can do.
(That isn't an issue if EF expects # before the parameter name instead of colon, that can be worked out easily.)
You can do it like this:
var sql = "INSERT INTO myTable (myField1, myField2) " +
"VALUES (#someValue, #someOtherValue);";
using (var cmd = new SqlCommand(sql, myDbConnection))
{
cmd.Parameters.AddWithValue("#someValue", someVariable);
cmd.Parameters.AddWithValue("#someOtherValue", someTextBox.Text);
cmd.ExecuteNonQuery();
}
What you absolutly must NOT do is:
var sql = "INSERT INTO myTable (myField1, myField2) " +
"VALUES ('" + someVariable + "', '" + someTextBox.Text + "');";
var cmd = new SqlCommand(sql, myDbConnection);
cmd.ExecuteNonQuery();
The problem with the second example is that it opens your code to an SQL Injection attack.
One (hacky but accurate?!) way with the original ":parameter" bind variable syntax is to call out to C and use Oracle OCI functions to do the parsing for you.
Prepare the statement with OCIStmtPrepare2() and then call
OCIStmtGetBindInfo() to get the variable names.
I have a slight issue, I have a ASP.NET Webforms application. I'm sending over a url?id=X were X is my database index or id.
I have a C# class file to run my SQL connection and query. Here is the code:
public DataTable ViewProduct(string id)
{
try
{
string cmdStr = "SELECT * Products WHERE Idx_ProductId = " + id;
DBOps dbops = new DBOps();
DataTable vpTbl = dbops.RetrieveTable(cmdStr, ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString);
return vpTbl;
}
catch (Exception e)
{
return null;
}
}
So as you can see my problem lies within string cmdStr = "SQL Query" + variable;
I'm passing over my index or id through the URL then requesting it and turning it into a string then using ViewProduct(productId).
I don't know what syntax or how to add the id into my C# string sql query. I've tried:
string cmdStr = "SELECT * Products WHERE Idx_ProductId = #0" + id;
string cmdStr = "SELECT * Products WHERE Idx_ProductId = {0}" + id;
also what I have currently to no avail.
I was so sure this would be a duplicate of some canonical question about parameterized queries in C#, but apparently there isn't one (see this)!
You should parameterize your query - if you don't, you run the risk of a malicious piece of code injecting itself into your query. For example, if your current code could run against the database, it would be trivial to make that code do something like this:
// string id = "1 OR 1=1"
"SELECT * Products WHERE Idx_ProductId = 1 OR 1=1" // will return all product rows
// string id = "NULL; SELECT * FROM UserPasswords" - return contents of another table
// string id = "NULL; DROP TABLE Products" - uh oh
// etc....
ADO.NET provides very simple functionality to parameterize your queries, and your DBOps class most assuredly is not using it (you're passing in a built up command string). Instead you should do something like this:
public DataTable ViewProduct(string id)
{
try
{
string connStr = ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
// #id is very important here!
// this should really be refactored - SELECT * is a bad idea
// someone might add or remove a column you expect, or change the order of columns at some point
cmd.CommandText = "SELECT * Products WHERE Idx_ProductId = #id";
// this will properly escape/prevent malicious versions of id
// use the correct type - if it's int, SqlDbType.Int, etc.
cmd.Parameters.Add("#id", SqlDbType.Varchar).Value = id;
using (SqlDataReader reader = cmd.ExecuteReader())
{
DataTable vpTbl = new DataTable();
vpTbl.Load(reader);
return vpTbl;
}
}
}
}
catch (Exception e)
{
// do some meaningful logging, possibly "throw;" exception - don't just return null!
// callers won't know why null got returned - because there are no rows? because the connection couldn't be made to the database? because of something else?
}
}
Now, if someone tries to pass "NULL; SELECT * FROM SensitiveData", it will be properly parameterized. ADO.NET/Sql Server will convert this to:
DECLARE #id VARCHAR(100) = 'NULL; SELECT * FROM SensitiveData';
SELECT * FROM PRoducts WHERE Idx_ProductId = #id;
which will return no results (unless you have a Idx_ProductId that actually is that string) instead of returning the results of the second SELECT.
Some additional reading:
https://security.stackexchange.com/questions/25684/how-can-i-explain-sql-injection-without-technical-jargon
Difference between Parameters.Add and Parameters.AddWithValue
SQL injection on INSERT
Avoiding SQL injection without parameters
How do I create a parameterized SQL query? Why Should I? (VB.NET)
How can I prevent SQL injection in PHP? (PHP specific, but many helpful points)
Is there a canonical question telling people why they should use SQL parameters?
What type Products.Idx_ProductId is?
Probably it is string, than you need to use quotes: "... = '" + id.Trim() + "'";
I tried to update a paragraph from mysql table,but i got error like this
"You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 's first-ever super-villainess."
My mysql Query
cmd.CommandText = "UPDATE `moviemaster` SET `Runtime`='" + runtime + "',`DateMasterId`='" + dateid + "',`Trailer`='" + trailer + "',`Synopsis`='" + synopsis + "' WHERE `MovieMasterId`='" + movieid + "'";
I got error in 'synopsis',it's a big data containing a large paragraph.If i romove 'Synopsis' section from the query,everything working fine.What exactly the problem.How can i resolve this?
#SonerGönül:Ok,fine.. then please show me an example of parameterised
query
Sure. I also wanna add a few best practice as well.
Use using statement to dispose your connection and command automatically.
You don't need to escape every column with `` characters. You should only escape if they are reserved keywords for your db provider. Of course, at the end, changing them to non-reserved words is better.
Do not use AddWithValue method. It may generate upexpected and surprising result sometimes. Use Add method overload to specify your parameter type and it's size.
using (var con = new SqlConnection(conString))
using(var cmd = con.CreateCommand())
{
cmd.CommandText = #"UPDATE moviemaster
SET Runtime = #runtime, DateMasterId = #dateid, Trailer = #trailer, Synopsis = #synopsis
WHERE MovieMasterId = #movieid";
cmd.Parameters.Add("#runtime", MySqlDbType.VarChar).Value = runtime; ;
cmd.Parameters.Add("#dateid", MySqlDbType.VarChar).Value = dateid;
cmd.Parameters.Add("#trailer", MySqlDbType.VarChar).Value = trailer;
cmd.Parameters.Add("#synopsis", MySqlDbType.VarChar).Value = synopsis;
cmd.Parameters.Add("#movieid", MySqlDbType.VarChar).Value = movieid;
// I assumed your column types are VarChar.
con.Open();
cmd.ExecuteNonQuery();
}
Please avoid using inline query. Your database can be subjected to SQL Injection. See this example, on what can be done using SQL Injection.
And use paramterized query instead. Here is the example taken from here. This way, even if your string has special characters, it will not break and let you insert/update/select based on parameters.
private String readCommand = "SELECT LEVEL FROM USERS WHERE VAL_1 = #param_val_1 AND VAL_2 = #param_val_2;";
public bool read(string id)
{
level = -1;
MySqlCommand m = new MySqlCommand(readCommand);
m.Parameters.AddWithValue("#param_val_1", val1);
m.Parameters.AddWithValue("#param_val_2", val2);
level = Convert.ToInt32(m.ExecuteScalar());
return true;
}
and finally, your query will become
cmd.CommandText = "UPDATE `moviemaster` SET `Runtime`= #param1,`DateMasterId`= #dateid, `Trailer`= #trailer,`Synopsis`= #synopsis WHERE `MovieMasterId`= #movieid";
cmd.Parameters.AddWithValue("#param1", runtime);
cmd.Parameters.AddWithValue("#dateid", dateid);
cmd.Parameters.AddWithValue("#trailer", trailer);
cmd.Parameters.AddWithValue("#synopsis", synopsis);
cmd.Parameters.AddWithValue("#movieid", movieid);
I'm trying to write a program using SQL and OleDB and I get an error while the Program is running.
the program first counts the number of rows in the table(access table which called 'tblCodons')
and storage the number as integer in j.
then the program stores all the rows (from a specific column which called 'codonsFullName') in comboBox1.
the code is below
I get this ERROR:System.Data.OleDb.OleDbException (0x80040E14): Invalid SQL statement;Required values' DELETE ',' INSERT ',' PROCEDURE ',' SELECT 'or' UPDATE
the code:
int j=0;
OleDbConnection conn1 = new OleDbConnection(connectionString);
conn1.Open();
string sqlCount= "SET #j= SELECT COUNT(tblCodons.codonsFullName) FROM tblCodons";
OleDbCommand counter = new OleDbCommand(sqlCount, conn1);
counter.ExecuteNonQuery();
conn1.Close();
OleDbConnection conn2 = new OleDbConnection(connectionString);
conn2.Open();
string sqlFill = "SELECT tblCodons.codonsFullName FROM tblCodons";
OleDbCommand fill = new OleDbCommand(sqlFill, conn2);
fill.ExecuteNonQuery();
OleDbDataReader dataReader = fill.ExecuteReader();
dataReader.Read();
for (int i = 0; i < j; i++)
{
comboBox1.Items.Add(dataReader.GetString(i));
}
You seem to need the count only for the loop. Also I do not understand why you are executing fill.ExecuteNonQuery() before executing is as a reader.
Also setting #j (if it did work) in a sql query has no scope to a local variable j in the code you are trying to set.
You should only need the following code (apologies for any syntax errors)
OleDbConnection conn2 = new OleDbConnection(connectionString);
conn2.Open();
string sqlFill = "SELECT tblCodons.codonsFullName FROM tblCodons";
OleDbCommand fill = new OleDbCommand(sqlFill, conn2);
OleDbDataReader dataReader = fill.ExecuteReader();
int j = 0;
if (dataReader.HasRows)
{
while(dataReader.Read())
{
comboBox1.Items.Add(dataReader.GetString(0));
j++;
}
}
Hope that helps
Leaving this answer here as an explanation for fixing your code as it currently exists, but also want to point out that I recommend going with Kamal's solution; it only queries the database once.
This line is probably your error:
string sqlCount= "SET #j= SELECT COUNT(tblCodons.codonsFullName) FROM tblCodons";
change to
string sqlCount= "SELECT COUNT(tblCodons.codonsFullName) FROM tblCodons";
You'll want to change your code to obtain the result of that first query like this:
j = counter.ExecuteScalar();
First, as Kamal Mentioned you can't directly set a variable from a sql query as you are trying to do and as the exception states only "SELECT" , "INSERT","UPDATE" and "DELETE" commands can be use in a query.
Second, I don't know why you need to get the record counts before getting the actual data but if it's really necessary you can write your query like this :
var query="SELECT COUNT(tblCodons.codonsFullName) FROM tblCodons;SELECT tblCodons.codonsFullName FROM tblCodons;";
Then you can execute both query using a single DataReader. When you execute DataReader.ExequteQuery() it will contain two results.the first one has access to the count and the second one has access to actual data.
Here's an example