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.
Related
How would I delete a row from a sql database, either with stored procedures or without, right now I have tried without, using a button press.
This is what I have so far, _memberid has been sent over from a differnt form from the database(For context).
private void btnDelete_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = Lib.SqlConnection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "Delete * From Members where MemberId = " + _memberId;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.DeleteCommand = cmd;
adapter.Fill(MembersDataTable); // Im fairly sure this is incorrect but i used it from old code
DialogResult = DialogResult.OK;
}
If you're trying to do a simple ADO.Net-based delete, then it would be somehting like his:
private void DeleteById(int memberId)
{
// or pull the connString from config somewhere
const string connectionString = "[your connection string]";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = new SqlCommand("DELETE FROM Members WHERE MemberId = #memberId", connection))
{
command.Parameters.AddWithValue("#memberId", memberId);
command.ExecuteNonQuery();
}
}
Use parameter to prevent SQL injection.
There are essentially three main things I'm seeing...
One
You don't need the * in the query. DELETE affects the whole row, so there's no need to specify columns. So just something like:
DELETE FROM SomeTable WHERE SomeColumn = 123
Two
There's no need for a SqlDataAdapter here, all you need to do is execute the query. For example:
cmd.ExecuteNonQuery();
The "non query" is basically a SQL command which doesn't query data for results. Inserts, updates, and deletes are generally "non queries" in this context. What it would return is simply the number of rows affected, which you can use to double-check that it matches what you expect if necessary.
Three
Don't do this:
cmd.CommandText = "Delete From Members where MemberId = " + _memberId;
This kind of string concatenation leads to SQL injection. While it looks intuitively like you're using _memberId as a query value, technically you're using it as executable code. It's less likely (though not impossible) to be a problem for numeric values, but it's a huge problem for string values because it means the user can send you any string and you'll execute it as code.
Instead, use query parameters. For example, you might do something like this:
cmd.CommandText = "Delete From Members where MemberId = #memberId";
cmd.Parameters.Add("#memberId", SqlDbType.Int);
cmd.Parameters["#memberId"].Value = _memberId;
This tells the database engine itself that the value is a value and not part of the executing query, and the database engine knows how to safely handle values.
You could use a DataAdapter, but since you aren't using a datatable, it's just easier to do it without like this:
var sql = "DELETE FROM Members WHERE MemberId=#MemberId";
using(var cmd = new SqlCommand(sql, Lib.SqlConnection))
{
cmd.Connection.Open();
cmd.Parameters.Add("#MemberId",SqlDbType.Int).Value = _memberId;
cmd.ExecuteNonQuery();
}
And if you are using Dapper, you can do this:
Lib.SqlConnection.Execute("DELETE FROM Members WHERE MemberId=#MemberId", new {MemberId=_memberId});
If you are still using DataTables, I would highly recommend you look into using this (or something like this) to simplify your database accesses. It'll make CRUD logic on a database a breeze, and your code will me a lot more maintainable because you can get rid of all the odd needs to do casting, boxing/unboxing, and reduce the chances of runtime bugs because of the use of magic strings that happens so often with DataTables (column names). Once you start working with POCO classes, you'll hate having to use DataTables. That said, there are a few places where DataTables are a better solution (unknown data structures, etc), but those are usually pretty rare.
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);
How do I make it so that my query only update the data I want?
Here's the current code
string query = string.Format("update Customer set title='{0}',[Name]='{1}'",titleComboBox2.Text,nameTextBox2.Text,"where ID="+idTextBox+"");
Apparently the last part of the query isn't working. Why it is that?
Because you didn't use any index argument as {2} for your third argument which is WHERE part.
That's why your query will be contain only update Customer set title='{0}',[Name]='{1}' part this will be update for your all rows since it doesn't have any filter.
Fun fact, you could see this as query if you would debug your code.
But more important
You should always use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
Let's assume you use ADO.NET;
using(var con = new SqlConnection(conString))
using(var cmd = con.CreateCommand())
{
cmd.CommandText = #"update Customer set title = #title, [Name] = #name
where ID = #id";
cmd.Paramter.Add("#title", SqlDbType.NVarChar).Value = titleComboBox2.Text;
cmd.Paramter.Add("#name", SqlDbType.NVarChar).Value = nameTextBox2.Text;
cmd.Paramter.Add("#id", SqlDbType.Int).Value = int.Parse(idTextBox.Text);
// I assumed your column types.
con.Open();
cmd.ExecuteNonQuery();
}
Currently your query does not use WHERE clause, because it is ignored by string.Format. You have 3 placeholder parameters, and you are using only {0} and {1}, so WHERE part is never added to the SQL query. Change your query to include WHERE clause, e.g. like this:
string query = string.Format("update Customer set title='{0}',[Name]='{1}' {2}",titleComboBox2.Text,nameTextBox2.Text,"where ID="+idTextBox.Text+"");
However, there is one very serious flaw in your code - it is vulnerable to SQL injection attack. There are hundreds of articles about it online, make sure to read about what that is and how to update your code accordingly (hint - parametrize queries)
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())
{
}
}
}