I usually use sql parameters with queries, but in this case I need to dynamically create more than just the parameters.
Could someone use injection on any of the variables? Aside from a stored procedure is there a simple way to protect against injection via code?
string whereClause = "WHERE " + filter.ToString() + " > " + nextStartPoint;
string orderBy = "ORDER BY " + filter.ToString() + " DESC";
ex
string sql = "SELECT TOP(" + numItemsToGet + ") * " +
"FROM Items " +
whereClause + " " +
orderBy;
Update
filter.ToString() is the actual column name
I'm surprised the following worked (partial ex)... I also thought you have to reference a column name with sql parameters.
cmd.Parameters.AddWithValue("Count", 10);
string sql = "SELECT TOP(#Count) * " +
Yes this is definitely subject to injection. If the user controls the filter parameter then it's very easy for them to inject bad SQL into your statement.
The simplest way to prevent an injection attack is to use SqlCommand to build up your command. It's designed to help prevent such attacks and will take the appropriate steps to protect your input
If you only have a finite set of possible filters, you can use an approach such as this, but this is a somewhat convoluted approach. I'd recommend using other tools, such as an OR mapper.
SET ROWCOUNT #numItemsToGet
select *
from Items
where
(
#ColumnANextStartPoint is null
or ColumnA > #ColumnANextStartPoint
) and (
#ColumnBNextStartPoint is null
or ColumnB > #ColumnBNextStartPoint
) and (
#ColumnCNextStartPoint is null
or ColumnC > #ColumnCNextStartPoint
)
order by
case #ColumnANextStartPoint when null then null else ColumnA end DESC,
case #ColumnBNextStartPoint when null then null else ColumnB end DESC,
case #ColumnCNextStartPoint when null then null else ColumnC end DESC
*My apologies, this code is untested.
The simple way to prevent SQL injection is to use parametrized queries like the following example:
SqlConnection someConnection = new SqlConnection(connection);
SqlCommand someCommand = new SqlCommand();
someCommand.Connection = someConnection;
someCommand.Parameters.Add(
"#username", SqlDbType.NChar).Value = name;
someCommand.Parameters.Add(
"#password", SqlDbType.NChar).Value = password;
someCommand.CommandText = "SELECT AccountNumber FROM Users " +
"WHERE Username=#username AND Password=#password";
someConnection.Open();
object accountNumber = someCommand.ExecuteScalar();
someConnection.Close();
There isn't enough information about the nature of filter and its string representations to rule it out. Possibly it's 100% safe because none of its possible values can cause injection, but possibly it's 100% unsafe because it's really easy to inject through it.
Related
I have an SQL request where I need to concatenate data into the request:
if (dataChoosen != "randomValue")
{
sCondition = " WHERE RandomField = '" + dataChoosen + "' ";
}
cd.CommandText = "SELECT xData FROM table " + sCondition + "GROUP BY xxx";
As I need to concatenate the condition, I don't think I can use a prepared request?
Also, I already tryparse the 'dataChoosed' value because it comes from a textbox and I need an integer. So is the the tryparse enough to prevent SQL injection?
I would just use parameters, there's no reason not to.
if (dataChoosed != "randomValue")
{
sCondition = " WHERE RandomField = #dataChoosed ";
}
cd.CommandText = "SELECT xData FROM table " + sCondition + "GROUP BY xxx";
cd.Parameters.Add("#dataChoosed", SqlDbType.VarChar).Value = dateChoosed;
No, you are not on the safe side. Even if dataChoosed is an innocent integer value, bad boys can hurt you with, say, negative value format:
// It's good old "-1", with a bit strange format
// (let use "delete from table commit;" as an injection)
string dataChoosed = "1'; delete from table commit; --1";
// A little hack: let "-" sign be...
CultureInfo hacked = new CultureInfo("en-US");
hacked.NumberFormat.NegativeSign = "1'; delete from table commit; --";
Thread.CurrentThread.CurrentCulture = hacked;
if (dataChoosed != "randomValue")
{
int v;
// since "1'; delete from table commit; --1" is of correct fotmat it will be parsed
if (int.TryParse(dataChoosed, out v))
sCondition = " WHERE RandomField = '" + dataChoosed + "' ";
}
cd.CommandText = "SELECT xData FROM table " + sCondition + "GROUP BY xxx";
And, woe! Where's my table? The command text will be
SELECT xData FROM table = '1'; delete from table commit; --1'GROUP BY xxx
which is efficently two queries:
SELECT xData FROM table = '1'; -- the innocent one
delete from table commit; -- an arbitrary query from the attacker
(I've removed commented out --1'GROUP BY xxx fragment)
Please, use parameters, do not tempt us. Please, notice, that you don't want to change code: all you have to do is to change the Regional Settings in your Windows.
Does [BLANK] protect against sql injection?
Unless [BLANK] is 'parameters' the answer is always no.
I have an sql query that I need change to parameters so I can avoid sql injection.
adapter.SelectCommand.CommandText = #"SELECT c.*,(Select Initials FROM users WHERE User_ID = c.CreatedByUser) AS CreatedBy, (SELECT Initials FROM users WHERE User_ID = c.ModifiedByUser) AS ModifiedBy FROM currency c WHERE c.Company_ID = " + Company_ID + " AND c.CurrencyCode = '" + Code.Replace("'", "''") + "' ORDER BY c.Description
adapter.SelectCommand.Parameters.Add(new MySqlParameter("company_ID", Company_ID));
adapter.SelectCommand.Parameters.Add(new MySqlParameter("code", Code));
I know for Company_ID I need to change it to WHERE c.Company_ID = ?company_ID but I am not sure what to do for c.CurrencyCode = '" + Code.Replace("'", "''") + "'
I just don't know how to change the Code.Replace part, since its not a simple as company_ID
As per here
Try using (for odbc for example):
cmd.Parameters.Add("?CURRENCY", OdbcType.VarChar, Code.Replace("'", "''"))
Odbc approach
OdbcCommand cmd = sql.CreateCommand();
cmd.CommandText = "SELECT UNIQUE_ID FROM userdetails WHERE USER_ID IN (?, ?)";
cmd.Parameters.Add("?ID1", OdbcType.VarChar, 250).Value = email1;
cmd.Parameters.Add("?ID2", OdbcType.VarChar, 250).Value = email2;
For oracle:
//create SQL and insert parameters
OracleCommand cmd = new OracleCommand("insert into daily_cdr_logs (message) values (:_message)", con);
cmd.Parameters.Add(new OracleParameter("_message", msg));
For mysql:
cmd = new MySqlCommand("SELECT * FROM admin WHERE admin_username=#val1 AND admin_password=PASSWORD(#val2)", MySqlConn.conn);
cmd.Parameters.AddWithValue("#val1", tboxUserName.Text);
cmd.Parameters.AddWithValue("#val2", tboxPassword.Text);
cmd.Prepare();
So a parameterized query (to me at least) generally means that you have created a stored procedure on your database and then use your code to execute the stored procedure while passing in the relevant parameters.
This has a couple of benefits
DRY - you don't have to repeat the query in code, you can just call the execute method and pass in the appropriate parameters
Helps prevent SQL injection - You can only modify the parameters which hopefully will be sanitized before being passed to the query
Here is how to create a stored procedure according to MSDN
and
Here is how to execute a a stored procedure according to MSDN
If you are determined to do it via LINQ, MSDN has what you are looking for here
EDIT: It seems you are concerned about sql-injection (which is good!), here is an article (again from MSDN) that covers that topic pretty extensively
I have the answer. c.CurrencyCode = '" + Code.Replace("'", "''") + "' simply changes to c.CurrencyCode = ?code
I have a query to insert a row into a table, which has a field called ID, which is populated using an AUTO_INCREMENT on the column. I need to get this value for the next bit of functionality, but when I run the following, it always returns 0 even though the actual value is not 0:
MySqlCommand comm = connect.CreateCommand();
comm.CommandText = insertInvoice;
comm.CommandText += "\'" + invoiceDate.ToString("yyyy:MM:dd hh:mm:ss") + "\', " + bookFee + ", " + adminFee + ", " + totalFee + ", " + customerID + ")";
int id = Convert.ToInt32(comm.ExecuteScalar());
According to my understanding, this should return the ID column, but it just returns 0 every time. Any ideas?
EDIT:
When I run:
"INSERT INTO INVOICE (INVOICE_DATE, BOOK_FEE, ADMIN_FEE, TOTAL_FEE, CUSTOMER_ID) VALUES ('2009:01:01 10:21:12', 50, 7, 57, 2134);last_insert_id();"
I get:
{"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 'last_insert_id()' at line 1"}
MySqlCommand comm = connect.CreateCommand();
comm.CommandText = insertStatement; // Set the insert statement
comm.ExecuteNonQuery(); // Execute the command
long id = comm.LastInsertedId; // Get the ID of the inserted item
[Edit: added "select" before references to last_insert_id()]
What about running "select last_insert_id();" after your insert?
MySqlCommand comm = connect.CreateCommand();
comm.CommandText = insertInvoice;
comm.CommandText += "\'" + invoiceDate.ToString("yyyy:MM:dd hh:mm:ss") + "\', "
+ bookFee + ", " + adminFee + ", " + totalFee + ", " + customerID + ");";
+ "select last_insert_id();"
int id = Convert.ToInt32(comm.ExecuteScalar());
Edit: As duffymo mentioned, you really would be well served using parameterized queries like this.
Edit: Until you switch over to a parameterized version, you might find peace with string.Format:
comm.CommandText = string.Format("{0} '{1}', {2}, {3}, {4}, {5}); select last_insert_id();",
insertInvoice, invoiceDate.ToString(...), bookFee, adminFee, totalFee, customerID);
Use LastInsertedId.
View my suggestion with example here: http://livshitz.wordpress.com/2011/10/28/returning-last-inserted-id-in-c-using-mysql-db-provider/
It bothers me to see anybody taking a Date and storing it in a database as a String. Why not have the column type reflect reality?
I'm also surprised to see a SQL query being built up using string concatenation. I'm a Java developer, and I don't know C# at all, but I'd wonder if there wasn't a binding mechanism along the lines of java.sql.PreparedStatement somewhere in the library? It's recommended for guarding against SQL injection attacks. Another benefit is possible performance benefits, because the SQL can be parsed, verified, cached once, and reused.
Actually, the ExecuteScalar method returns the first column of the first row of the DataSet being returned. In your case, you're only doing an Insert, you're not actually querying any data. You need to query the scope_identity() after you're insert (that's the syntax for SQL Server) and then you'll have your answer. See here:
Linkage
EDIT: As Michael Haren pointed out, you mentioned in your tag you're using MySql, use last_insert_id(); instead of scope_identity();
string queryString4 = "UPDATE Table1 SET currentMoney =currentMoney + '" + money + "'WHERE accountNo='" + recipientNo + "';";
user1 & user2 have $100
user1 transfer $5 to user2.
user1 now have $95 & user2 now have $1005
Somehow it did not calculate properly. Im suspecting the code above because I did a querystring3 which is minus instead of a plus and it works. However querystring4 is a bit of a problem.
You're appending a string here:
currentMoney =currentMoney + '" + money + "'
Let's assume that money is 5, this becomes:
currentMoney =currentMoney + '5'
In many languages this will result in an implicit conversion of the numeric value to a string value, so:
100 + '5' = '1005'
Then when you store it, I guess it was implicitly converted back to a numeric value? It's odd to me that you didn't receive an error message during any of this.
In any event, you're looking at two fixes:
For now, get rid of those single-quotes and treat the numeric value as a numeric value instead of a string value.
Don't build your queries by concatenating strings. The problem you're facing now is one of the lesser problems you'll encounter by doing this. Exposing yourself to SQL injection attacks is another, more significant problem. Use query parameters instead of string concatenation.
This is a textbook case. You need a transaction to encapsulate the two commands.
You also need to use a parameterized query and not a string concatenation.
decimal sumOfTransaction = 5m;
string creditAccount = "123456ABC";
string debitAccount = "ABC9876543";
using(TransactionScope scope = new TransactionScope())
using(SqlConnection cn = new SqlConnection(connectionString))
{
string upd1 = #"UPDATE Table1 SET currentMoney = currentMoney + #amount
WHERE accountNo=#account";
string upd2 = #"UPDATE Table1 SET currentMoney = currentMoney - #amount
WHERE accountNo=#account";
cn.Open();
using(SqlCommand cmd = new SqlCommand(upd1, cn);
{
cmd.Parameters.AddWithValue("#amount", sumOfTransaction);
cmd.Parameters.AddWithValue("#account", creditAccount);
cmd.ExecuteNonQuery();
cmd.CommandText = upd2;
cmd.Parameters["#account"].Value = debitAccount);
cmd.ExecuteNonQuery();
}
scope.Complete();
}
The use of a transaction is mandatory here, because you don't want, for ANY reason to credit some amout of money to one account and for whatever reason miss to debit the other account.
(In real cases you need a lot more than this. For example, this code lacks of any checks against the amount available in the debit account).
Of course your initial error is due to the fact that you are treating your amount as it was a string but this is plainly wrong. When dealing with money values you should not rely on implicit conversions of any kind.
compose sql string in this way is a very bad practice. You should use Sql Parameters instead.
Anyway, try this way:
string queryString4 = "UPDATE Table1 SET currentMoney =currentMoney + (" + money + ") WHERE accountNo='" + recipientNo + "';";
But i strongly advise you to use parameters.
if you please help me i am having a problem in sql code asp.net C#.
my error is:
System.Data.SqlClient.SqlException was unhandled by user code
Message=Incorrect syntax near ')'.
and my query code goes as follows:
string query = #"insert into ReviewPaper(Overall_Rating,Paper_ID,Conference_Role_ID,Deitails)
values(" + 0 + "," + ListBox4.SelectedValue +"," + ListBox1.SelectedValue + "," + null + ")";
You can't insert null like that way. Use parameterized query.
string query = "insert into ReviewPaper(Overall_Rating,Paper_ID,Conference_Role_ID,Deitails)
values (#overall_rating,#paper_id,#conference_role_id,#details)";
cmd=new SqlCommand(query,cn);
cmd.Parameters.AddWithValue("#overall_rating",0);
cmd.Parameters.AddWithVaule("#paper_id",ListBox2.SelectedValue);
cmd.Parameters.AddWithValue("#conference_role_id",Listbox1.SelectedValue);
cmd.Parameters.AddWithValue("#details",DBNull.Value);
Yes, as everybody else said already, you can't use null the way you are doing it but there are more serious issues than that:
Your sql statement is prone to SQL Injection attacks because you are not parametrizing your query
If you are not inserting a value into a column, simply don't list the column! This will work:
string query = #"insert into ReviewPaper(Overall_Rating,Paper_ID,Conference_Role_ID)
values(" + 0 + "," + ListBox4.SelectedValue +"," + ListBox1.SelectedValue +")";
I think the null is probably making things angry:
string query = #"insert into ReviewPaper(Overall_Rating,Paper_ID,Conference_Role_ID,Deitails)
values(0," + ListBox4.SelectedValue +"," + ListBox1.SelectedValue + ",null)";
You'll notice I made your 0 part of the string and made the null part of the string (instead of concatenating integer 0 and a NULL value with the string)
What you are doing with this example is you are creating a SQL string that you plan on sending to the Database that will be executed there. When you are making your string the result of the string is something like...
"insert into ReviewPaper(Overall_Rating,Paper_ID,Conference_Role_ID,Deitails) values(0, someValueFromListbox4,someOtherValueFromListbox1,)"
You will notice that the final parameter is missing. To fix this try this...
string query = #"insert into ReviewPaper(Overall_Rating,Paper_ID,Conference_Role_ID,Deitails)
values(" + 0 + "," + ListBox4.SelectedValue +"," + ListBox1.SelectedValue + ",NULL)";
Here is another example using string.format which I would reccommend
string query = String.format("Insert into ReviewPaper(Overall_Rating,Paper_ID,Conference_Role_ID,Deitails) Values(0,{0},{1},NULL)", ListBox4.SelectedValue, ListBox1.SelectedValue);
Try putting the null within the speech marks so the end looks like ",null)";