Really confused about sql injection - c#
Got this detail page about bug that might lead to sql injection
URL encoded GET input classid was set to 1 AND 3*2*1=6 AND 608=608
Tests performed:
1*1*1*1 => TRUE
1*608*603*0 => FALSE
11*5*2*999 => FALSE
1*1*1 => TRUE
1*1*1*1*1*1 => TRUE
11*1*1*0*1*1*608 => FALSE
1 AND 5*4=20 AND 608=608 => TRUE
1 AND 5*4=21 AND 608=608 => FALSE ... (line truncated)
And this is the source code that might cause the matter:
if (!string.IsNullOrEmpty(Request.QueryString["classid"]))
{
string tSql = #" SELECT [Award_ID],[Award_Name],[Award_Info],[Award_Pic],[Award_Num],[Award_MoneyCost],[Award_MoneyGet],[Award_Type],[Award_AddDate],[Award_Hot],[Award_OnLineTime],[AwardProP],[PrizeSlidePic],[PrizeDetailPic],[PrizeBigSlidePic],[IsTop],[ClassID] FROM dbo.Web_Award WHERE ClassID={0} ";
DataTable data = DbSession.Default.FromSql(string.Format(tSql, Request.QueryString["classid"])).ToDataTable();
if (data.Rows.Count > 0)
{
rptList.DataSource = data;
rptList.DataBind();
}
}
else
{
string tSql = #" SELECT [Award_ID],[Award_Name],[Award_Info],[Award_Pic],[Award_Num],[Award_MoneyCost],[Award_MoneyGet],[Award_Type],[Award_AddDate],[Award_Hot],[Award_OnLineTime],[AwardProP],[PrizeSlidePic],[PrizeDetailPic],[PrizeBigSlidePic],[IsTop],[ClassID] FROM dbo.Web_Award ";
DataTable data = DbSession.Default.FromSql(tSql).ToDataTable();
if (data.Rows.Count > 0)
{
rptList.DataSource = data;
rptList.DataBind();
}
}
Can anyone tell me how to deal with this...thanks a lot!
Now i have modifeid my code to
if (!string.IsNullOrEmpty(Request.QueryString["classid"]))
{
//string tSql = #" SELECT [Award_ID],[Award_Name],[Award_Info],[Award_Pic],[Award_Num],[Award_MoneyCost],[Award_MoneyGet],[Award_Type],[Award_AddDate],[Award_Hot],[Award_OnLineTime],[AwardProP],[PrizeSlidePic],[PrizeDetailPic],[PrizeBigSlidePic],[IsTop],[ClassID] FROM dbo.Web_Award WHERE ClassID={0} ";
string tSql = "SELECT [Award_ID],[Award_Name],[Award_Info],[Award_Pic],[Award_Num],[Award_MoneyCost],[Award_MoneyGet],[Award_Type],[Award_AddDate],[Award_Hot],[Award_OnLineTime],[AwardProP],[PrizeSlidePic],[PrizeDetailPic],[PrizeBigSlidePic],[IsTop],[ClassID] FROM dbo.Web_Award WHERE ClassID = #ClassID";
//DataTable data = DbSession.Default.FromSql(string.Format(tSql, Request.QueryString["classid"])).ToDataTable();
SqlConnection connection = new SqlConnection("Server=(local);Integrated Security=SSPI;database=DaysQP");
connection.Open();
SqlCommand command = new SqlCommand(tSql, connection);
command.Parameters.Add(new SqlParameter("#ClassId", System.Data.SqlDbType.Int));
command.Parameters["#ClassID"].Value = 1;
using (SqlDataReader dr = command.ExecuteReader())
{
var data = new DataTable();
data.Load(dr);
if (data.Rows.Count > 0)
{
rptList.DataSource = data;
rptList.DataBind();
}
}
connection.Close();
}
else
{
string tSql = #" SELECT [Award_ID],[Award_Name],[Award_Info],[Award_Pic],[Award_Num],[Award_MoneyCost],[Award_MoneyGet],[Award_Type],[Award_AddDate],[Award_Hot],[Award_OnLineTime],[AwardProP],[PrizeSlidePic],[PrizeDetailPic],[PrizeBigSlidePic],[IsTop],[ClassID] FROM dbo.Web_Award ";
DataTable data = DbSession.Default.FromSql(tSql).ToDataTable();
if (data.Rows.Count > 0)
{
rptList.DataSource = data;
rptList.DataBind();
}
}
But the problem still exists..
Finally sovled the problem by using parameterized queries!
if (!string.IsNullOrEmpty(Request.QueryString["classid"]))
{
int number;
bool result = Int32.TryParse(Request.QueryString["classid"], out number);
if (result == false)
{
return;
}
//string tSql = #" SELECT [Award_ID],[Award_Name],[Award_Info],[Award_Pic],[Award_Num],[Award_MoneyCost],[Award_MoneyGet],[Award_Type],[Award_AddDate],[Award_Hot],[Award_OnLineTime],[AwardProP],[PrizeSlidePic],[PrizeDetailPic],[PrizeBigSlidePic],[IsTop],[ClassID] FROM dbo.Web_Award WHERE ClassID={0} ";
string tSql = "SELECT [Award_ID],[Award_Name],[Award_Info],[Award_Pic],[Award_Num],[Award_MoneyCost],[Award_MoneyGet],[Award_Type],[Award_AddDate],[Award_Hot],[Award_OnLineTime],[AwardProP],[PrizeSlidePic],[PrizeDetailPic],[PrizeBigSlidePic],[IsTop],[ClassID] FROM dbo.Web_Award WHERE ClassID = #ClassID";
//DataTable data = DbSession.Default.FromSql(string.Format(tSql, Request.QueryString["classid"])).ToDataTable();
SqlConnection connection = (SqlConnection)DbSession.Default.CreateConnection();
//SqlConnection("Server=(local);Integrated Security=SSPI;database=DaysQP");
connection.Open();
SqlCommand command = new SqlCommand(tSql, connection);
command.Parameters.Add(new SqlParameter("#ClassId", System.Data.SqlDbType.Int));
command.Parameters["#ClassID"].Value = number;
using (SqlDataReader dr = command.ExecuteReader())
{
var data = new DataTable();
data.Load(dr);
if (data.Rows.Count > 0)
{
rptList.DataSource = data;
rptList.DataBind();
}
}
connection.Close();
}
The potential for injection would be here:
string tSql = #" SELECT [Award_ID],[Award_Name],[Award_Info],[Award_Pic],[Award_Num],[Award_MoneyCost],[Award_MoneyGet],[Award_Type],[Award_AddDate],[Award_Hot],[Award_OnLineTime],[AwardProP],[PrizeSlidePic],[PrizeDetailPic],[PrizeBigSlidePic],[IsTop],[ClassID] FROM dbo.Web_Award WHERE ClassID={0} ";
DataTable data = DbSession.Default.FromSql(string.Format(tSql, Request.QueryString["classid"])).ToDataTable();
You're expecting the query to return Web_Award table records whose classId matches Request.QueryString["classid"]
What happens if the value of Request.QueryString["classid"] is something like:
1 or 1=1
then the query becomes:
select award_id,..... from web_awards where classId=1 or 1=1
and you end up returning data that you never meant to.
This, in essence, is sql injection which you probably read up a bit more about. Using stored procedures or parameterized queries prevents this sort of attack.
Related
How to check SqlParameter is true with If
I want to check sqlparameter is true on if statements like this; string sql1 = "SELECT * FROM users WHERE mail=#mail and passwd is null"; string sql2 = "SELECT * FROM users WHERE mail=#mail and passwd=#password"; SqlParameter prm1 = new SqlParameter("mail", txtMail.Text.Trim()); SqlParameter prm2 = new SqlParameter("password", txtPassword.Text.Trim()); if (sql1 == true) { MessageBox.Show("yes"); } else if (sql2 == true) { MessageBox.Show("yes2"); } { MessageBox.Show("no"); }
It's rather unclear what you are trying to do, but it looks like you might want code like this: const string query = #" SELECT CASE WHEN passwdHash is null THEN 1 ELSE 2 END FROM users WHERE mail = #mail and (passwdHash is null OR passwdHash = #passwdHash); "; using (var conn = new SqlConnection(yourConnString)) using (var comm = new SqlCommand(query, conn)) { comm.Parameters.Add("#mail", SqlDbType.VarChar, 200).Value = txtMail.Text.Trim(); comm.Parameters.Add("#passwordHash", SqlDbType.Binary, 32) = SaltAndHashPassword(txtPassword.Text.Trim()); conn.Open(); var result = comm.ExecuteScalar() as int?; conn.Close(); if (result == 1) { MessageBox.Show("yes"); } else if (result == 2) { MessageBox.Show("yes2"); } else { MessageBox.Show("no"); } } Note the following using blocks on all Sql objects Specify parameter types and lengths explicitly Hash the password and compare that instead Don't select *, just pass back a single column with a result Close the connection before blocking with a MessageBox
SQL query on ADO.net limitation with 2100+ parameters
I am trying to implement an ADO.NET code which executes the SQL query with multiple parameters. Looks like SQL parameter limit is 2100 and does not accept more than this limit. How do I achieve with my below code to have this accept more than the limitation. I am finding it difficult to understand the implementations when validating online articles related how to send the queries in subsets or chunks to fulfill my request. This is my code: using (Connection = new SqlConnection(CS)) { Connection.Open(); string query = "SELECT FamilyID, FullName, Alias FROM TABLE (nolock) WHERE FamilyID IN ({0})"; var stringBuiler = new StringBuilder(); var familyIds = new List<string>(); string line; while ((line = TextFileReader.ReadLine()) != null) { line = line.Trim(); if (!familyIds.Contains(line) & !string.IsNullOrEmpty(line)) { familyIds.Add(line); } } var sqlCommand = new SqlCommand { Connection = Connection, CommandType = CommandType.Text }; var index = 0; // Reset the index var idParameterList = new List<string>(); foreach (var familyId in familyIds) { var paramName = "#familyId" + index; sqlCommand.Parameters.AddWithValue(paramName, familyId); idParameterList.Add(paramName); index++; } sqlCommand.CommandText = String.Format(query, string.Join(",", idParameterList)); var dt = new DataTable(); using (SqlDataReader sqlReader = sqlCommand.ExecuteReader()) { dt.Load(sqlReader); } try { if (dt.Rows.Count > 0) { OutputdataGridView.DataSource = lstDownloadOwnerOutput; OutputdataGridView.ColumnHeadersDefaultCellStyle.Font = new Font(DataGridView.DefaultFont, FontStyle.Bold); OutputdataGridView.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; Gridviewdisplaylabel.Text = "Total no of rows: " + this.OutputdataGridView.Rows.Count.ToString(); } else if (dt.Rows.Count == 0) { MessageBox.Show("Data returned blank!!!"); } } catch (Exception Ex) { if (Connection != null) { Connection.Close(); } MessageBox.Show(Ex.Message); } }
Having a WHERE IN clause with 2100, or even 100, parameters is generally not good coding practice. You might want to consider putting those values into a separate bona fide table, e.g. families (ID int PK, ...) Then, you may rewrite your query as: SELECT FamilyID, FullName, Alias FROM TABLE (nolock) WHERE FamilyID IN (SELECT ID FROM families); You could also express the above using an EXISTS clause or a join, but all three approaches might just optimize to a very similar query plan anyway.
You can just add a table load call every 2000 parameters in your code: var index = 0; // Reset the index var idParameterList = new List<string>(); var dt = new DataTable(); foreach (var familyId in familyIds) { var paramName = "#familyId" + index; sqlCommand.Parameters.AddWithValue(paramName, familyId); idParameterList.Add(paramName); index++; if (index > 2000) { sqlCommand.CommandText = String.Format(query, string.Join(",", idParameterList)); using (SqlDataReader sqlReader = sqlCommand.ExecuteReader()) dt.Load(sqlReader); sqlCommand.Parameters.Clear(); idParameterList.Clear(); index = 0; } } if (index > 0) { sqlCommand.CommandText = String.Format(query, string.Join(",", idParameterList)); using (SqlDataReader sqlReader = sqlCommand.ExecuteReader()) dt.Load(sqlReader); }
For dynamic sql like this, I generally recommend using a Table-Valued Parameter. It does require a bit of setup: you have to create a user-defined Type in the DB to hold the values, but that is a fairly trivial operation: CREATE TYPE PrimaryKeyType AS TABLE ( VALUE INT NOT NULL ); We generally use these in conjunction with stored procedures: CREATE PROCEDURE dbo.getFamily(#PrimaryKeys PrimaryKeyType READONLY) AS SELECT FamilyID, FullName, Alias FROM TABLE (nolock) INNER JOIN #PrimaryKeys ON TABLE.FamilyID = #PrimaryKeys.Value GO However, you can also use inline SQL if you prefer. Assigning the values to the stored proc or inline parameter is fairly straightforward, but there is one gotcha (more later): public static void AssignValuesToPKTableTypeParameter(DbParameter parameter, ICollection<int> primaryKeys) { // Exceptions are handled by the caller var sqlParameter = parameter as SqlParameter; if (sqlParameter != null && sqlParameter.SqlDbType == SqlDbType.Structured) { // The type name may look like DatabaseName.dbo.PrimaryKeyType, // so remove the database name if it is present var parts = sqlParameter.TypeName.Split('.'); if (parts.Length == 3) { sqlParameter.TypeName = parts[1] + "." + parts[2]; } } if (primaryKeys == null) { primaryKeys = new List<int>(); } var table = new DataTable(); table.Columns.Add("Value", typeof(int)); foreach (var wPrimaryKey in primaryKeys) { table.Rows.Add(wPrimaryKey); } parameter.Value = table; } The thing to watch out for here is the naming of the parameter. See the code in the method above that removes the database name to resolve this issue. If you have dynamic SQL, you can generate a correct parameter using the following method: public static SqlParameter CreateTableValuedParameter(string typeName, string parameterName) { // Exceptions are handled by the caller var oParameter = new SqlParameter(); oParameter.ParameterName = parameterName; oParameter.SqlDbType = SqlDbType.Structured; oParameter.TypeName = typeName; return oParameter; } Where typeName is the name of your type in the DB.
SQL Server : change NULL to blank on select statement for all columns
I am calling a SQL Server database through my Web API which is then sending the data to my webpage only problem is when it does that some of the values are null and it errors. How would I make it so when I pull the data using SQL that I make it check every column and row for nulls and change them to '' without writing ISNULL(a.BuildingNumber, '') or am I trying to attempt something that isn't possible. Or is it possible to loop through the DataTable that I make when C# gets the data from SQL Server, if so would something like a foreach loop work instead. using(SqlCommand cmd = new SqlCommand(query, con)) { cmd.CommandType = CommandType.Text; var dtb = new DataTable(); var da = new SqlDataAdapter(cmd); da.Fill(dtb); return Json(dtb); } That is what I do after my query to return it.
You could check in the following way string ServiceName = reader["ColumnNameFromQuery"] != System.DBNull.Value ? reader["ColumnNameFromQuery"].ToString() : ""; An Example string QueryGetData = "select Name as Name , Age as Age from tableone;"; SqlCommand cmd = m_SqlConnection.CreateCommand(); cmd.CommandType = CommandType.Text; cmd.CommandText = QueryGetData; SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { List<string> Data = new List<string>(); string strName = reader["Name"] != System.DBNull.Value ? reader["ServiceName"].ToString() : ""; string strName = reader["Age"] != System.DBNull.Value ? reader["Age"].ToString() : ""; //If Int Set Default as Zero int nIntValue = reader["IntColumnName"] != System.DBNull.Value ? Convert.ToInt32(reader["IntColumnName"].ToString()) : 0; }
I got the desired result by doing it in my javascript like so getData() { var search_url = "http://10.0.1.96/testwebapi/api/case/" + this.state.ApiUrl + "/" + this.props.match.params.id var _this = this; this.serverRequest = axios .get(search_url) .then(function (result) { for (var property in result.data[0]) { if (result.data[0][property] === null) { result.data[0][property] = ''; } } _this.setState({ [_this.state.ApiUrl]: result.data[0] }, function () { }); }); }
how to write a single sql query based on combobox selection?
I have a winform in which user input values through a combobox. I am assigning combobox value to search db. If there is no selection then the SQL server query should not use that column to filter. example - if (string.IsNullOrEmpty(combobox1.text)) { .....Select * from country } else if (combobox1.selectedindex > -1) { ....Select * from country where city_name = combobox.text } Is there a way to write a single query instead of using this multiple 'IF' conditions in case where user selects or doesn't select a value from combobox.
It is important to parameterize as well: private const string _select = "select * from country"; void DoSomething() { string sql = string.Empty; if (combobox1.SelectedIndex > -1) { command.Parameters.AddWithValue("#1", (string)combobox1.SelectedValue); sql = " where city_name = #1"; } sql = _select + sql; command.CommandText = sql; command.Execute... } #un-lucky asked me how would I deal with many conditions - here is one way var conditions = new List<string>(); if (/* condition 1*/) { command.Parameters.AddWithValue("#2", (string)cboN.SelectedItem); conditions.Add("col1 = #2"); } if (/* condition 2*/) { command.Parameters.AddWithValue("#3", textBoxN.Text); conditions.Add("col2 = #3"); } if (conditions.Count > 0) sql = _select + " where " + string.Join(" AND ", conditions.ToArray());
You can use the shorthand if only if you have two conditions: string query = string.Format("Select * from country{0}", string.IsNullOrEmpty(combobox1.text) ? "" : " where city_name = " + combobox1.text); Hope it helps!
I think you have to try something like this with parameterization: StringBuilder queryBuilder = new StringBuilder("Select * from country Where 1=1 "); SqlCommand cmdSql = new SqlCommand(); if (combobox1.selectedindex > -1) { queryBuilder.Append(" And city_name = #city_name "); cmdSql.Parameters.Add("#city_name", SqlDbType.VarChar).Value = combobox.text; } else if(Condition 2) { queryBuilder.Append(" And column2 = #col2 "); cmdSql.Parameters.Add("#col2", SqlDbType.VarChar).Value = "some Value here; } // Build the query like this cmdSql.CommandText= = queryBuilder.ToString(); cmdSql.Connection = conObject; // Here you can execute the command
I have a sample, try it string select = this.combobox1.GetItemText(this.combobox1.SelectedItem); cm1 = new SqlCommand("Select * from country where city_name=#select or #select is null", con); cm1.Parameters.Add("#select", SqlDbType.NVarChar, 50); cm1.Parameters["#select"].Value = select; dap = new SqlDataAdapter(cm1); ds = new System.Data.DataSet(); dap.Fill(ds, "DATABASE"); //DataGridView1.DataSource = ds.Tables[0]; get data
c# fill dataTable
I have a textbox window1.xaml that works as a searchbox. I am using a integrated sql database in my project and I have a dataset.xsd as well with tabel adapters and queries. Whenever I need to query the database I do something like this: BooksTableAdapter tableAdapterBooks = new BooksTableAdapter(); dataSetLibrary.BooksDataTable dataTableBooks; dataTableBooks = tableAdapterBooks.getDataByTitle(searchText); For this searchbox I have the following code. string[] allWords = txtSearch.Text.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); string sql = "SELECT Books.ISBN, Books.Title, Books.Tag, Books.Image, Books.photoType, Publishers.Name AS publisherName FROM Books INNER JOIN Publishers ON Books.codPublisher = Publishers.codPublisher WHERE "; using (SqlCommand command = new SqlCommand()) { for (int i = 0; i < allWords.Length; ++i) { if (i > 0) sql += "OR "; string paramName = "#param" + i.ToString(); sql += string.Format("(Books.Title LIKE {0}) ", paramName); command.Parameters.AddWithValue(paramName, allWords[i] + "%"); } command.CommandText = sql; } How can I use the command to query my database and fill dataTableBooks?
After hours around this, I have come up with this solution. private SqlConnection sqlConn = new SqlConnection(); private System.Data.DataSet dataSet = new System.Data.DataSet(); private System.Data.DataTable dataTable; private System.Data.DataRow dataRow; private SqlCommand search(string searchParam, int searchOption) { SqlCommand command = new SqlCommand(); string sql; string[] allWords = searchParam.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (searchOption == 1) { sql = "SELECT Livros.ISBN, Livros.Titulo, Livros.Tema, Livros.Resumo, Livros.Imagem, Livros.fotoTipo, Editoras.Nome AS nomeEditora FROM Livros INNER JOIN Editoras ON Livros.codEditora = Editoras.codEditora WHERE "; } else { sql = "SELECT Livros.ISBN, Livros.Titulo, Livros.Tema, Livros.Resumo, Livros.Imagem, Livros.fotoTipo, Editoras.Nome AS nomeEditora FROM Livros INNER JOIN livrosAutores ON Livros.ISBN = livrosAutores.ISBN INNER JOIN Autores ON livrosAutores.idAutor = Autores.idAutor INNER JOIN Editoras ON Livros.codEditora = Editoras.codEditora WHERE "; } using (command) { for (int i = 0; i < allWords.Length; ++i) { if (i > 0) { sql += "OR "; } if (searchOption == 1) { sql += string.Format("(Livros.Titulo LIKE '%{0}%') ", allWords[i]); } else { sql += string.Format("(Livros.Autor LIKE '%{0}%') ", allWords[i]); } } command.CommandText = sql; } return command; } protected void Bind() { sqlConn.ConnectionString = Properties.Settings.Default.BibliotecaConnectionString; string connectionString = sqlConn.ConnectionString.ToString(); SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(search(searchText, searchOption).CommandText, connectionString); sqlDataAdapter.Fill(dataSet, "livrosTitulo"); dataTable = dataSet.Tables["livrosTitulo"]; dataGrid.DataContext = dataTable.DefaultView; }