How to pass a C# variable with apostrophe through MySql - c#

I'm having a problem inputting variables into my database. I've seen other posts on how to pass a variable through by just escaping it, but those solutions do not apply because I am getting my variable's through an API. I'm cycling though data with a foreach loop by the way.
level = "" + x.Account_Level + "";
name = "" + x.name + "";
command.CommandText = "INSERT INTO `data` (`level`, `name`) VALUES(" + level + ", " + name + ")";
command.ExecuteNonQuery();
Sometimes, a variable will come back with an apostrophe and will screw up the code. Is it possible to insert a slash before every apostrophe or is there a way like in PHP to just push the whole variable through with single quotes? Thanks!
Edit:
Would this work? I think I need to add the i to change the name of the parameter each loop, due to it claiming the parameter as already declared.
using (var web = new WebClient())
{
web.Encoding = System.Text.Encoding.UTF8;
var jsonString = responseFromServer;
var jss = new JavaScriptSerializer();
var MatchesList = jss.Deserialize<List<Matches>>(jsonString);
string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;";
MySqlConnection connect = new MySqlConnection(connectString);
MySqlCommand command = connect.CreateCommand();
int i = 1;
connect.Open();
foreach (Matches x in MatchesList)
{
command.CommandText = "INSERT INTO `data` (`level`, `name`) VALUES(?level" + i + ", ?name" + i + ")";
command.Parameters.AddWithValue("level" + i, x.Account_Level);
command.Parameters.AddWithValue("mode" + i, x.name);
command.ExecuteNonQuery();
i++;
}
connect.Close();
}

The quick and dirty fix is to use something like:
level = level.Replace("'","whatever");
but there are still problems with that. It won't catch other bad characters and it probably won't even work for edge cases on the apostrophe.
The best solution is to not construct queries that way. Instead, learn how to use parameterised queries so that SQL injection attacks are impossible, and the parameters work no matter what you put in them (within reason, of course).
For example (off the top of my head so may need some debugging):
MySqlCommand cmd = new MySqlCommand(
"insert into data (level, name) values (?lvl, ?nm)", con);
cmd.Parameters.Add(new MySqlParameter("lvl", level));
cmd.Parameters.Add(new MySqlParameter("nm", name));
cmd.ExecuteNonQuery();

Related

C# sql query stop at cmd.execute

Hello I've made a method that takes search parameters and then returns a query. This was done using ADO.net now what I have noticed is that my method abruptly ends on the cmd.execute. Here is my method.
public class SQLStringSearchService
{
string connectionString = string
public async Task<List<TransactionJournal>> SearchArchivedTransactionJournal(DateTime transactionDate, string Region, string MCC, string MerchantID, string TxnCurrency, string TerminalID, decimal TxnAmount, string BIN, string MsgType, string MaskedPan, string ProcessingCode, string ClearPan, string ResponseCode, string AuthorizationCode, string EntryMode, [FromQuery] PaginationDTO pagination)
{
var monthString = string.Empty;
if (transactionDate.Month < 10)
{
monthString = transactionDate.Month.ToString().Insert(0,"0");
}
else
{
monthString = transactionDate.Month.ToString();
}
var DbTable = "TRANSACTIONJOURNAL_" + transactionDate.Year.ToString() + monthString;
var queryable = new List<TransactionJournal>();
using (SqlConnection connection = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand())
{
connection.Open();
cmd.Connection = connection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "Select * From Table = #Table Where TransactionDateTime <= #DateTime and AcquirerID = #Region and MerchantCategoryCode = #MCC and MerchantID = #MerchantId and Currency = #TxnCurrency and TerminalID = #TerminalId and " +
"TransactionAmount = #TxnAmount and Bin = #Bin and MessageType = #MsgType and ProcessingCode = #ProcessingCode and PANM = #Panm and PAN = #Pan and ResponseCode = #ResponseCode and AuthorizationCode = #AuthCode and ResponseCode = #ResponseCode";
cmd.Parameters.AddWithValue("#Table", DbTable);
cmd.Parameters.AddWithValue("#DateTime", transactionDate);
cmd.Parameters.AddWithValue("#Region", Region);
cmd.Parameters.AddWithValue("#MCC", MCC);
cmd.Parameters.AddWithValue("#MerchantId", MerchantID);
cmd.Parameters.AddWithValue("#TxnCurrency", TxnCurrency);
cmd.Parameters.AddWithValue("#TerminalId", TerminalID);
cmd.Parameters.AddWithValue("#TxnAmount", TxnAmount);
cmd.Parameters.AddWithValue("#Bin", BIN);
cmd.Parameters.AddWithValue("#MsgType", MsgType);
cmd.Parameters.AddWithValue("#ProcessingCode", ProcessingCode);
cmd.Parameters.AddWithValue("#Panm", MaskedPan);
cmd.Parameters.AddWithValue("#Pan", ClearPan);
cmd.Parameters.AddWithValue("#ResponseCode", ResponseCode);
cmd.Parameters.AddWithValue("#AuthCode", AuthorizationCode);
cmd.Parameters.AddWithValue("#ResponseCode", ResponseCode);
queryable = (List<TransactionJournal>)cmd.ExecuteScalar();
cmd.Dispose();
}
}
var orderedQuery = queryable.OrderByDescending(x=> x.TransactionDateTime).AsQueryable();
await HttpContextExtensions.GetPage<TransactionJournal>(orderedQuery, pagination.Page, pagination.QuantityPerPage);
return await orderedQuery.Paginate(pagination).ToListAsync();
}
}
I have tried debugging and the method just ends, I can't seem to figure out what the problem could be. If someone could show me an example of maybe the correct way to so this I would greatly appreciate it. Thanks!
Code doesn't just stop but any time it seems like it does, it's probably encountering an exception that isn't handled properly, inside of a coding construct that cannot tolerate it - I most often see this if there is some background threading involved, the thread hits an exception, it's stopped and the exception is transferred to whatever was managing the thread (which then does nothing with it). The thread just goes away and VS stops tracking it. You might find it switches to another thread but it does look like the code "just stopped"- probably we'd find that this method was called without await or its in a backgrounder worker that doesn't retrieve the exception
All in, this code has some flaws and cannot work. As per the comments the sql is invalid. You also wouldn't/couldn't executeScalar and hope to get a list out of it. By definition scalar is a single value, the top left cell of any result set- there's no scope for it to be a list of values. If you want a list you have to executeReader and fill the list ...
If you enable break when thrown (go in the debug menu , look for exceptions window, put a tick next to Common Language Runtime - now as soon as any exception is raised, handled or not, the code will stop. You'll probably find now that your code is stopping on a sql exception, but at least you can fix it and move on. It's recommend you at least wrap your executeXxx in a try catch...
You seem to have an invalid query. You can't pass table as a variable the way you're trying to. To achieve that type of functionality you have to use string.Fromat.
Try something like:
using (var cmd = new SqlCommand())
{
connection.Open();
cmd.Connection = connection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = string.Join(
string.Fromat("SELECT * FROM {0} ",DbTable),
"WHERE TransactionDateTime <= #DateTime ",
"AND AcquirerID = #Region ",
"AND MerchantCategoryCode = #MCC",
"AND MerchantID = #MerchantId ",
"AND Currency = #TxnCurrency ",
"AND TerminalID = #TerminalId ",
"AND TransactionAmount = #TxnAmount ",
"AND Bin = #Bin ",
"AND MessageType = #MsgType ",
"AND ProcessingCode = #ProcessingCode ",
"AND PANM = #Panm ",
"AND PAN = #Pan ",
"AND ResponseCode = #ResponseCode ",
"AND AuthorizationCode = #AuthCode ",
"AND ResponseCode = #ResponseCode");
cmd.Parameters.AddWithValue("#DateTime", transactionDate);
cmd.Parameters.AddWithValue("#Region", Region);
cmd.Parameters.AddWithValue("#MCC", MCC);
cmd.Parameters.AddWithValue("#MerchantId", MerchantID);
cmd.Parameters.AddWithValue("#TxnCurrency", TxnCurrency);
cmd.Parameters.AddWithValue("#TerminalId", TerminalID);
cmd.Parameters.AddWithValue("#TxnAmount", TxnAmount);
cmd.Parameters.AddWithValue("#Bin", BIN);
cmd.Parameters.AddWithValue("#MsgType", MsgType);
cmd.Parameters.AddWithValue("#ProcessingCode", ProcessingCode);
cmd.Parameters.AddWithValue("#Panm", MaskedPan);
cmd.Parameters.AddWithValue("#Pan", ClearPan);
cmd.Parameters.AddWithValue("#ResponseCode", ResponseCode);
cmd.Parameters.AddWithValue("#AuthCode", AuthorizationCode);
cmd.Parameters.AddWithValue("#ResponseCode", ResponseCode);
queryable = (List<TransactionJournal>)cmd.ExecuteScalar();
}
You don't have to use the string.Join the way I did. I just wanted to split the query in parts to make it a bit more readable for me. You can format that all sorts of ways.
When possible, I would recommend that you write your queries and test them in SQL server or local instance. Then you can use it to create your command text string.
Something like this might have helped you spot the issue:
DECLARE #Table varchar(50) = 'TRANSACTIONJOURNAL_202003',
#DateTime datetime = GETDATE(),
-- ... And so on with the rest of the variables you need
-- you have lots of variables
-- you get the gist
SELECT * FROM Table = #Table
WHERE TransactionDateTime <= #DateTime
AND AcquirerID = #Region
AND MerchantCategoryCode = #MCC
AND MerchantID = #MerchantId
AND Currency = #TxnCurrency
AND TerminalID = #TerminalId
AND TransactionAmount = #TxnAmount
AND Bin = #Bin
AND MessageType = #MsgType
AND ProcessingCode = #ProcessingCode
AND PANM = #Panm
AND PAN = #Pan
AND ResponseCode = #ResponseCode
AND AuthorizationCode = #AuthCode
AND ResponseCode = #ResponseCode;
One more thing. I would recommend not using SELECT *. Instead I'd explicitly write out the column names. If they ever add another column to that table it could break things. Specially if they change the order of the columns.
Hope this helps.

Using Parameter in Sql Query [duplicate]

I got a runtime error saying "Must declare the table variable "#parmTableName". Meaning having table name as sql parameter in the sql-statement is not allowed.
Is there a better option or suggestion than allowing sql injection attack? I don't want to do this C# script for sql statement " DELETE FROM " + tableName + " ";
using(var dbCommand = dbConnection.CreateCommand())
{
sqlAsk = "";
sqlAsk += " DELETE FROM #parmTableName ";
sqlAsk += " WHERE ImportedFlag = 'F' ";
dbCommand.Parameters.Clear();
dbCommand.Parameters.AddWithValue("#parmTableName", tableName);
dbConnection.Open();
rowAffected = dbCommand.ExecuteNonQuery();
}
Go for a white list. There can only be a fixed set of possible correct values for the table name anyway - at least, so I'd hope.
If you don't have a white list of table names, you could start with a whitelist of characters - if you restrict it to A-Z, a-z and 0-9 (no punctuation at all) then that should remove a lot of the concern. (Of course that means you don't support tables with odd names... we don't really know your requirements here.)
But no, you can't use parameters for either table or column names - only values. That's typically the case in databases; I don't remember seeing one which did support parameters for that. (I dare say there are some, of course...)
As others have already pointed out that you can't use Table Name and Fields in Sql Parameter, one thing that you can try is to escape table name using SqlCommandBuilder, like:
string tableName = "YourTableName";
var builder = new SqlCommandBuilder();
string escapedTableName = builder.QuoteIdentifier(tableName);
using (var dbCommand = dbConnection.CreateCommand())
{
sqlAsk = "";
sqlAsk += " DELETE FROM " + escapedTableName; //concatenate here
sqlAsk += " WHERE ImportedFlag = 'F' ";
dbCommand.Parameters.Clear();
dbConnection.Open();
rowAffected = dbCommand.ExecuteNonQuery();
}
(sqlAsk is string, right?) if it's right so let's try this:
using(var dbCommand = dbConnection.CreateCommand())
{
sqlAsk = "";
sqlAsk += " DELETE FROM <table_name> ";
sqlAsk += " WHERE ImportedFlag = 'F' ";
string table_name = "Your table name here"; //<- fill this as u need
sqlAsk = sqlAsk.Replace("<table_name>", table_name); // it will replace <table_name> text to string table_name
dbConnection.Open();
rowAffected = dbCommand.ExecuteNonQuery();
}

Why is my response always 0 after using parameters in OleDB?

I have a simple .aspx login website and I use OleDB for local validation.
Here is my problem: After finding SQL Injection vulnerability, I decided to use parameters. But after using parameters my response is always "0" (Same as "Authenticated=false"). But if I don't use parameters, my response is "1" (Same as "Authenticated=true").
Here some pics while debugging:
Without parameters where the response=1 (Authenticated):
With code:
string idstr = Request.QueryString["id"];
idstr.Replace("''", "");
string passpath = Request.QueryString["password"];
passpath.Replace("''", "");
OleDbConnection connect = new OleDbConnection();
OleDbCommand cmd = new OleDbCommand();
connect.ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0; Data Source= C:\Users\hugow_000\Desktop\OSGS_Kantine_htm_design\Kantine_data.accdb; Persist Security Info = False;";
cmd.Connection = connect;
connect.Open();
cmd.CommandText = "select * from User_data where Stamnummer="+idstr+" and Wachtwoord="+ passpath;
OleDbDataReader read = cmd.ExecuteReader();
int code = 0;
while (read.Read())
{
code = code + 1;
}
if (code == 1)
{
Response.Redirect("~/AuthKeyGEN.aspx?Auth=true&id=" + idstr + "&password=" + passpath + "");
}
if (code > 1)
{
Response.Redirect("~/Login.aspx?response=0");
}
if (code < 1)
{
Response.Redirect("~/Login.aspx?response=0");
}
}
And with parameters where the response is 0 (Not Authenticated):
And with code:
string idstr = Request.QueryString["id"];
idstr.Replace("''", "");
string passpath = Request.QueryString["password"];
passpath.Replace("''", "");
OleDbConnection connect = new OleDbConnection();
OleDbCommand cmd = new OleDbCommand();
connect.ConnectionString = #"Provider=Microsoft.ACE.OLEDB.12.0; Data Source= C:\Users\hugow_000\Desktop\OSGS_Kantine_htm_design\Kantine_data.accdb; Persist Security Info = False;";
cmd.Connection = connect;
connect.Open();
cmd.CommandText = "select * from User_data where Stamnummer=#idstr and Wachtwoord=#passpath";
cmd.Parameters.Add("#idstr", OleDbType.BSTR).Value = idstr;
cmd.Parameters.Add("#passpath", OleDbType.BSTR).Value = passpath;
OleDbDataReader read = cmd.ExecuteReader();
int code = 0;
while (read.Read())
{
code = code + 1;
}
if (code == 1)
{
Response.Redirect("~/AuthKeyGEN.aspx?Auth=true&id=" + idstr + "&password=" + passpath + "");
}
if (code > 1)
{
Response.Redirect("~/Login.aspx?response=0");
}
if (code < 1)
{
Response.Redirect("~/Login.aspx?response=0");
}
}
I am using the same credentials in both scenarios,
So why is my response always 0 if I use parameters in here?
Thanks in advance!
Doesn't look anything wrong but try using OleDbType.VarChar instead of OleDbType.BSTR since both the parameter are of string type; like
cmd.Parameters.Add("#idstr", OleDbType.VarChar).Value = idstr;
cmd.Parameters.Add("#passpath", OleDbType.VarChar).Value = passpath;
Also as a side note, instead of using select * use a count() query like below in which case you can use ExecuteScalar() rather than using ExecuteReader()
"select count(*) from User_data
where Stamnummer=#idstr and Wachtwoord=#passpath";
Ms Access uses ? as parameter place holders and the order is important (your order is correct). The parameter objects can be named as the name is ignored by the engine so it really does not matter but might make for more readable code. See OleDbCommand.Parameters as reference.
cmd.CommandText = "select 1 from User_data where Stamnummer = ? and Wachtwoord= ?";
Also change the parameter types as #Rahul had pointed out to VarChar.
General recommendations
Wrap your connection in a using block.This ensures your connection is always closed even when an Exception is encountered.
Like #Rahul said use ExecuteScalar instead of ExecuteReader. Use either COUNT(*) or hardcode 1 as the result: select 1 from User_data ...
Never ever store passwords as plain text, ever! This is horrible practice and makes for a very unsecure app. I have submitted a complete solution to creating a password hash that you could copy/paste and use directly.

Index was outside the bounds of the array exception

Here is my code to get data from a flat file and insert into SQL Server. It is generating an exception (Index was outside the bounds of the array).
string path = string.Concat(Server.MapPath("~/TempFiles/"), Fileupload1.FileName);
string text = System.IO.File.ReadAllText(path);
string[] lines = text.Split(' ');
con.Open();
SqlCommand cmd = new SqlCommand();
string[] Values = new string[3];
foreach (string line1 in lines)
{
Values = line1.Split(';');
string query = "INSERT INTO demooo VALUES ('" + Values[0] + "','" + Values[1] + "','" + Values[2] + "')";
cmd = new SqlCommand(query,con);
cmd.ExecuteNonQuery();
}
The exception happens because one of your lines has less than three elements separated with a semicolon. Even though you declare Values as a String array of three elements, affecting the variable to the result of String.Split() function makes that irrelevant: your array will have whatever length the returned array has. If it's less, your code will definitely fail.
If it's not supposed to happen I suggest you do an assertion in your code to help you debugging:
// ...
Values = line1.Split(';');
// the following will make the debugger stop execution if line.Length is smaller than 3
Debug.Assert(line1.Length >= 3);
// ...
As a side note, I should mention making a batch INSERT would be far more efficient. Also your way of declaring and reaffecting cmd variable isn't quite correct. And finally you should call String.Replace on your values to make sure any apostrophes is doubled. Otherwise your code will be open for SQL injection attacks.
Some details on how your code is behaving at run-time:
// This line declares a variable named Values and sets its value to
// a new array of strings. However, this new array is never used
// because the loop overwrites Values with a new array before doing
// anything else with it.
string[] Values = new string[3];
foreach (string line1 in lines)
{
Values = line1.Split(';');
// At this point in the code, whatever was previously stored in Values has been
// tossed on the garbage heap, and Values now contains a brand new array containing
// the results of splitting line1 on semicolons.
// That means that it is no longer safe to assume how many elements the Values array has.
// For example, if line1 is blank (which often happens at the end of a text file), then
// Values will be an empty array, and trying to get anything out of it will throw an
// exception
string query = "INSERT INTO demooo VALUES ('" + Values[0] + "','" + Values[1] + "','" + Values[2] + "')";
cmd = new SqlCommand(query,con);
cmd.ExecuteNonQuery();
}
Similar to how Values keeps getting overwritten, that SqlCommand that's created outside the loop will also never get used. It's safe to put both of these declarations inside the loop instead. The following code does that, and also adds some error checking to make sure that a usable number of values was retrieved from the line. It will simply skip any lines that aren't long enough - if that's not OK then you might need to create some more complex error-handling code of your own.
foreach(string line in lines)
{
string[] values = line.split[';'];
if(values.Length >= 3)
{
string query = "INSERT INTO demooo VALUES ('" + Values[0] + "','" + Values[1] + "','" + Values[2] + "')";
using (SqlCommand command = new SqlCommand(query, con))
{
cmd.ExecuteNonQuery();
}
}
}
As one final note, the above code might be vulnerable to hackers if you were using it in something like a Web application. Think about what command might get sent to the server if you were processing a file that looked like this:
1;2;3
4;5;6
7;8;9') DROP TABLE demooo SELECT DATALENGTH('1
A safer option is to use parameterized queries, which will help protect against this kind of attack. They do this by separating the command from its arguments, which helps protect you against passing in values for arguments that look like SQL code. An example of how to set things up that way would look more like this:
string query = "INSERT INTO demooo VALUES (#val1, #val2, #val3);
using (var command = new SqlCommand(query, con))
{
command.Parameters.AddWithValue("#val1", Values[0]);
command.Parameters.AddWithValue("#val2", Values[1]);
command.Parameters.AddWithValue("#val3", Values[2]);
command.ExecuteNonQuery();
}
Try this.
string path = string.Concat(Server.MapPath("~/TempFiles/"), Fileupload1.FileName);
string text = System.IO.File.ReadAllText(path);
string[] lines = text.Split(' ');
con.Open();
string[] Values;
foreach (string line1 in lines)
{
Values = line1.Split(';');
if (Values.Length >= 3)
{
string query = "INSERT INTO demooo VALUES ('" + Values[0] + "','" + Values[1] + "','" + Values[2] + "')";
}
else
{
//Some error occured
}
using (var cmd = new SqlCommand(query,con))
{
cmd.ExecuteNonQuery();
}
}

sql like query does not work

I am trying to retrieve video name from my database where the subject of the video is like my subject to search.
I try the like query but it was not return values.
Can you kindly give the suggestions.
I am using c# with sql server.
Here is my code.
if (con.State == ConnectionState.Open)
con.Close();
con.Open();
string s1 = textBox1.Text;
cmd = new SqlCommand("select Video_Name,subject from Videos where subject like '%"+ s1 +" % ' " ,con);
//cmd = new SqlCommand("select Video_Name from Videos where subject='"+ s1+"' ", con);
SqlDataReader dr = cmd.ExecuteReader();
ArrayList a = new ArrayList();
label2.Visible = true;
label3.Visible = true;
//if (dr.Read())
{
while (dr.Read())
{
a.Add(dr[0].ToString());
}
foreach (string n in a)
{
comboBox1.Items.Add(n);
}
MessageBox.Show("Search succeded");
}
Use a parameterized query
string s1 = textBox1.Text;
cmd = new SqlCommand("select Video_Name,subject from Videos where subject like #video",con);
cmd.Parameters.AddWithValue("#video", "%" + s1 + "%");
In this way you avoid the Sql Injection problem and your command text is more readable.
This will help also in formatting your command text without subtle typing errors and without the need to add quotes around strings. With a parameter, the burden to correctly quoting the parameter value is passed to the framework code that knows better how to do it correctly.
By the way, you could avoid the second loop setting the combobox.Datasource property to the ArrayList variable a
comboBox1.Datasource = a;
Maybe it is because you have a space after the last % and its '
"select Video_Name,subject from Videos where subject like '%"+ s1 +">> % ' "<<
try something like this
"select Video_Name,subject from Videos where subject like '%"+s1+"%'"
cmd = new SqlCommand("select Video_Name,subject from Videos where subject like #vdnam",con);
cmd.Parameters.AddWithValue("#vdnam", "%" + VdName + "%");
if (dr.HasRows)
{
while (dr.Read())
{
a.Add(dr[0].ToString());
}
comboBox1.Datasource= a.List();
MessageBox.Show("Search succeded");
}
Steve's answer is of course right.
The main problem is here, your query parameter is inside single quotes. In quotes, SQL will recognize it as a string literal and never sees it as a parameter.

Categories

Resources