I used SQLite. The user will pull the days from checkbox and I'll show it in data grid view but the date is recorded as day and time so I have to use like instead of in command.
DataSet dataSet122;
listBox1.Items.Clear();
SQLiteConnection connection = new SQLiteConnection("Data Source =log.sqlite;Version=3;");
string search = checkBoxComboBox1.Text;
string[] array = search.Split(',');
for (int i = 0; i < array.Length; i++)
{
array[i] = "'" + array[i] + "'";
}
string names = String.Join(",", array);
listBox2.Items.Add(names);
string query = "SELECT * FROM Gemkay1 WHERE ZAMAN LIKE (" + names + ")";
command = new SQLiteCommand(query, connection);
connection.Open();
adapter = new SQLiteDataAdapter(command);
dataSet122 = new DataSet();
adapter.Fill(dataSet122, "Gemkay1");
dataGridViewSummary1.DataSource = dataSet122.Tables["Gemkay1"];
SQL syntax for all people where name ends with SMITH or WRIGHT:
WHERE name LIKE '%SMITH' OR name LIKE '%WRIGHT'
LIKE is not the same as IN - it accepts a single string argument on the right hand side. If you want multiple LIKEs you must repeat the LIKE clause separated by OR
IN can be used with multiple string but it does not accept wildcards:
WHERE name IN ('playwright', 'cartwright', 'shipwright')
If you try and put a wildcard in it will literally match that character.
-
As an aside, don't make SQL like you're doing there, with string concatenation of the values. Concatenate parameters in instead and give them values, for example:
var names = new []{"%wright", "%smith"};
var sql = new SqliteCommand("SELECT * FROM t WHERE 1=0 ");
for(int p = 0; p<names.Length; p++){
sql.CommandText += " OR name like #p" + p;
sql.Parameters.AddWithValue("#p"+p, names[p]);
}
This I what I mean when I say "concatenate parameters in, then give them a value".
If you ever work with sqlserver read this blog post
Use IN operator to select data where multiple values
"SELECT * FROM Gemkay1 WHERE ZAMAN IN ('2021-02-01','2021-02-02')";
to ignore time from date you can use date function:
"SELECT * FROM Gemkay1 WHERE date(ZAMAN) IN ('2021-02-01','2021-02-02')";
See SQLite date and time functions documentation for more info.
Related
How can I pass a columns name by parameter,
follow an example
DataTable dt = new DataTable();
// Here I fill my datatable
for (int i = 0; i < dt.Rows.Count; i++)
{
for (int j = 0; j < dt.Columns.Count; j++)
{
string columnsname = dt.Rows[i][dt.columns[j].toString()].toString();
SqlCommand comando = new SqlCommand();
comando.commandText = "UPDATE Sales.Store SET #columnname = #demographics where id = #id";
comando.Parameters.Add(new SqlParameter("#columnname", columname));
comando.Parameters.Add(new SqlParameter("#dados2", dados2));
comando.ExecuteNonQuery();
comando.Clear();
comando.Dispose()
}
}
This doesn't work, but I have 88 columns, and I need update all data in every 88 columns in each row.
You cannot parameterize column names.
To do what you want you will need to resort to dynamic SQL.
Well, if you have 30,000 rows with 88 columns, and you need to update all 88 columns, you probably want to rethink your database schema.
Itay.
I have figured out a way to include a work around for parametrized column names. I had the same problem but came up with a different way and since I would be the only one using the column names then I believe this is still a safe bet.
String sqlcomm = "SELECT * FROM Asset WHERE " + assetColName + " = ";
command.CommandText = sqlcomm + "$assetColValue";
//command.CommandText = #"SELECT * FROM Asset WHERE $assetColName = '$assetColValue'";
//command.Parameters.AddWithValue("$assetColName", assetColName);
command.Parameters.AddWithValue("$assetColValue", assetColValue);
As you can see from the code above. I tried almost what you did which I then had to comment out. I then concatenated strings together and was able to use my parametrized column name and value which then the value is securely added. The column name however is not secured but this is a method that only I will be using so its still somewhat safe. You can add regular expressions if you want to be more secure but you get the idea of the fix.
Just concatenate the sql string:
"UPDATE Contracts set " + columnName + " = #columnValue where ID = #ID"
Where column name is a string that represents a column in the table
I was given a task to rewrite an old web API.
This API reads SQL queries from the database.
There's literally a view with "Queries" in the name which contains "SqlText" column.
SELECT SqlText FROM Queries WHERE QueryID = 123
The "SqlText" contains only simple SQL queries in the format SELECT [columns] FROM [table] by convention.
The query is altered depending on the URL parameters in the request. The result of this query is then shown as result.
string parsedColumns = ParseColumns(queryRow); //contains "Column1, Column2";
string parsedTable = ParseTable(queryRow); //contains "SomeTable"
string requestColumns = HttpContext.Request["columns"];
string sqlColumns = requestColumns ?? parsedColumns;
string col1Condition = HttpContext.Request["Column1"]
string col2Condition = HttpContext.Request["Column2"]
string sqlQuery = "SELECT " + sqlColumns
+ " FROM " + parsedTable
+ " WHERE Column1 = " + col1Condition
+ " AND Column2 = " + col2Condition;
This is obvious SQL injection issue so I started rewritting it.
Now there are three other problems.
I cannot change the structure of the database or the convention
The database is either Oracle or SQL Server
I don't know how to correctly work with the "columns" URL parameter to avoid SQL injection.
It's easy to convert the URL parameters in the WHERE clause to the SQL parameters for both SQL Server and Oracle.
SQL Server
var sqlCommand = new SqlCommand("SELECT * FROM SomeTable WHERE Condition1 = #con1 AND Condition2 = #con2");
Oracle
var oracleCommand = new OracleCommand("SELECT * FROM SomeTable WHERE Condition1 = :con1 AND Condition2 = :con2");
Column identifiers
The problem is with the HttpContext.Request["columns"]. I still need to somehow alter the SQL query string with URL parameters which I don't like at all.
To simplify the issue, let's consider a single column from URL request.
string column = HttpContext.Request["column"];
var cmd = new SqlCommand($"SELECT {column} FROM ...");
I know that in SQL Server the identifier can be surrounded by braces. So my line of thinking is that I'm safe if I strip all braces from the column.
string column = HttpContext.Request["column"];
column = column.Replace("[", "").Replace("]", "");
column = $"[{column}]";
var cmd = new SqlCommand($"SELECT {column} FROM ...");
Oracle uses quotation marks.
string column = HttpContext.Request["column"];
column = column.Replace("\"", "");
column = $"\"{column}\"";
var cmd = new OracleCommand($"SELECT {column} FROM ...");
The question
Is this sql-injection safe enough?
Or is this use case inherently sql-injection unsafe?
Since you are working with a basic program design that you cannot change what about just trying to add edits to the input to look for injection elements. For example if the input is a column name it will need to have a maximum length of 30 (before 12.x) characters and should not contain a semicolon or the strings " OR" or " AND" in them. While not a perfect solution this should be practical solution.
I have problem with types mismatch - I think. I have application which connects with database and sends query. That is how it works:
string wartosc1 = "'letters'";
NpgsqlCommand command9 = new NpgsqlCommand("SELECT * FROM RESOURCES WHERE TYPE = "+wartosc1, conn);
but when I try to execute it, there is answer:
System.FormatException: Input string was not in correct format.
I suppose that there is problem with type of variable because when I just input:
SELECT * FROM RESOURCES WHERE TYPE ='letters'
Everything is ok.
Any ideas?
You need to use parameters to pass in the value to the query.
Read http://msdn.microsoft.com/en-us/library/yy6y35y8(v=vs.110).aspx on how to do that.
var wartosc1 = "letters";
var command9 = new NpgsqlCommand("SELECT * FROM RESOURCES WHERE TYPE = #type", conn);
command9.Parameters.Add("#type", wartosc1);
Because when you write;
"SELECT * FROM RESOURCES WHERE TYPE = " + wartosc1
Your command will be like;
SELECT * FROM RESOURCES WHERE TYPE = letters
which is wrong because I suppose your TYPE column is some text type. If you solve this an easy way, you can add your wartosc1 variable inside single quotes like;
"SELECT * FROM RESOURCES WHERE TYPE = '" + wartosc1 + "'"
But please don't use this way.
You should always use parameterized queries in your commands. It prevents, forget to use some quotes, commas etc.. But more important this kind of string concatenations are open for SQL Injection attacks.
string wartosc1 = "letters";
NpgsqlCommand command9 = new NpgsqlCommand("SELECT * FROM RESOURCES WHERE TYPE = #type", conn);
command9.Parameters.AddWithValue("#type", wartosc1);
Here is an example of string Interpolation using several variables and a date:
var dt = DateTime.Now.AddDays(-30);
string wartosc1 = "letters";
string myStatement = $#"
SELECT *
FROM RESOURCES res
WHERE res.DATE_EXAMPLE >= '{dt}'
AND res.TYPE = '{wartosc1}'
"
BEWARE This sql string IS open to sql injection, simply by setting
wartosc1 = "somevalue' AND someOtherStatement 'thenDoSomethingBeforeApostrophe";
However, it may be that your environment doesn't need to worry about that... the apostrophes aren't necessary around an int, but forget it around a datetime, and you'll throw errors.
In the following sample, I build a query to do a bulk insert into a MySQL database:
const string QUERY = "INSERT INTO contacts (first_name,last_name) VALUES{0};";
public string BuildQuery(IEnumerable<contact> contacts)
{
List<string> values = new List<string>();
foreach (var contact in contacts)
{
values.Add(string.Format("('{0}','{1}')", contact.first_name, contact.last_name));
}
return string.Format(QUERY, string.Join(",", values));
}
The result might look something like this:
INSERT INTO contacts (first_name,last_name) VALUES("J","Kappers"),("A","Temple")
What can I do to write a safer query that isn't prone to SQL Injection?
const string QUERY = "INSERT INTO contacts (first_name,last_name) VALUES" +
BuildQuery(c, contacts);
public string BuildQuery(MySQLCommand c, IEnumerable<contact> contacts)
{
List<string> values = new List<string>();
string query = null;
int i = 0;
foreach (var contact in contacts)
{
i++;
query += "(#firstName" + i + ", #lastName" + i + ")";
c.Parameters.AddWithValue("#firstName" + i, contact.first_name);
c.Parameters.AddWithValue("#lastName" + i, contact.last_name);
if(i < contacts.Count)
query += ",";
}
return query
}
You can see a relevant thread here!. I must have missed somethin trivial, but thats trivial for u to fix. Of course you know what happens when contacts has no elements. I dont see more edge cases. Btw, mind u there is a limit to how many such parameters you can add depending on mysql's max allowed packet size. You can change it, or take care of not exceeding that limit. Cheers! :)
You can escape your MySQL command arguments almost the same way as in normal SQL command. Here is example from official MySQL manual
private void PrepareExample()
{
MySqlCommand cmd = new MySqlCommand("INSERT INTO mytable VALUES (?val)", myConnection);
cmd.Parameters.Add( "?val", 10 );
cmd.Prepare();
cmd.ExecuteNonQuery();
cmd.Parameters[0].Value = 20;
cmd.ExecuteNonQuery();
}
P.S. Whatever you choose - never user string manipulation to add/insert parameters to SQL command. This is main source for SQL-Injections attacks
Ok, I have a list that consists of a bunch of values from a sql query, that part works fine. What I want to do is use the items in that list to tell another query what to look for. So, what it is saying is that, it should return all columns from CMMReports where PartNumber is like %listItem1..2...3%, Any advice?
List<string> ImportedParts = GetImportedPartNumbers();
string query = "SELECT * FROM CMMReports WHERE (RacfId IS NULL OR RacfId = '') AND (FilePath NOT LIKE '%js91162%') AND PartNumber LIKE %" + ImportedParts + "% ORDER BY CreatedOn DESC;";
Not that I condone this as you should be using parameterized queries. However, this should work:
StringBuilder partNumbers = new StringBuilder();
foreach (string queryValue in ImportedParts)
{
string q = "PartNumber LIKE '%" + queryValue + "%'";
if (string.IsNullOrEmpty(partNumbers.ToString())
{
partNumbers.Append(q);
}
else
{
partNumbers.Append(" OR " + q);
}
}
string query = string.Format("SELECT * FROM CMMReports WHERE (RacfId IS NULL OR RacfId = '') " +
"AND (FilePath NOT LIKE '%js91162%') AND ({0}) " +
"ORDER BY CreatedOn DESC;", partNumbers.ToString());
You might look up the IN clouse for SQL that way you get the answer for the parts that SQL Server can find in the database. Using WHERE x = y for all the items means that if one item can't be found the whole query returns nothing.
I would consider doing this in a stored procedure and passing in your list as an Xml parameter.
See the following article for more info on using Xml parameters in a stored proc:
Passing lists to SQL Server 2005 with XML Parameters - By Jon Galloway
Form there you can easily use your list data inside your stored proc using the Xml syntax and treat it almost as another table of data.
Untested, but you should get the idea:
List<string> ImportedParts = GetImportedPartNumbers();
SqlCommand cmd = myConnection.CreateCommand();
cmd.CommandText = "SELECT * FROM CMMReports WHERE (RacfId IS NULL OR RacfId = '') AND (FilePath NOT LIKE '%js91162%') AND (";
int i = 0;
foreach (string part in ImportedParts) {
cmd.AddParameterWithValue("#param" + i.ToString(), "%" + part + "%");
if (i != 0) cmd.CommandText += " OR"
cmd.CommandText += " PartNumber LIKE #param" + i.ToString();
i++;
}
cmd.CommandText += ") ORDER BY CreatedOn DESC;";
This solution uses a parameterized query instead of just appending strings in the SQL, which is considered a potential security risk.