I'm working with a datagrid and adapter that correspond with an MSAccess table through a stored query (named "UpdatePaid", 3 paramaters as shown below) like so:
OleDbCommand odc = new OleDbCommand("UpdatePaid", connection);
OleDbParameter param;
odc.CommandType = CommandType.StoredProcedure;
param = odc.Parameters.Add("v_iid", OleDbType.Double);
param.SourceColumn = "I";
param.SourceVersion = DataRowVersion.Original;
param = odc.Parameters.Add("v_pd", OleDbType.Boolean);
param.SourceColumn = "Paid";
param.SourceVersion = DataRowVersion.Current;
param = odc.Parameters.Add("v_Projected", OleDbType.Currency);
param.SourceColumn = "ProjectedCost";
param.SourceVersion = DataRowVersion.Current;
odc.Prepare();
myAdapter.UpdateCommand = odc;
...
myAdapter.Update();
It works fine...but the really weird thing is that it didn't until I put in the odc.Prepare() call.My question is thus: Do I need to do that all the time when working with OleDb stored procs/queries? Why? I also have another project coming up where I'll have to do the same thing with a SqlDbCommand... do I have to do it with those, too?
This is called, oddly enough, a prepared statement, and they're actually really nice. Basically what happens is you either create or get a sql statement (insert, delete, update) and instead of passing actual values, you pass "?" as a place holder. This is all well and good, except what we want is our values to get passed in instead of the "?".
So we prepare the statement so instead of "?", we pass in parameters as you have above that are going to be the values that go in in place of the place holders.
Preparing parses the string to find where parameters can replace the question marks so all you have to do is enter the parameter data and execute the command.
Within oleDB, stored queries are prepared statements, so a prepare is required. I've not used stored queries with SqlDB, so I'd have to defer to the 2 answers previous.
I don't use it with SqlDbCommand. It seems as a bug to me that it's required. It should only be nice to have if you're going to call a procedure multiple times in a row. Maybe I'm wrong and there's a note in documentation about providers that love this call too much.
Are you using the JET OLEDB Provider? or MSDASQL + JET ODBC?
You should not need to call Prepare(), but I believe that's driver/provider dependent.
You definitely don't need to use Prepare() for System.Data.SqlClient.
Related
Currently I simply don't allow apostrophe's at all (along with other character's as you can see) with this, reiterated for each field:
foreach(char c in Comments)
{
if(c=='\'' || c=='$' || c=='\"' || c=='&' || c=='%' || c=='#' || c=='-' || c=='<' || c=='>')
{
errorMessage = "You have entered at least one invalid character in the \"Comments\" field. Invalid characters are: [\'], [\"], [&], [$], [#], [-], [<], [>], and [%]";
}
}
I've been coding this for a while, and I am getting better, but the real problem is that, while I am sure there is a way to effectively "strip-out" or otherwise validate the user input, I am not sure which approach is best and probably wouldn't until a security crisis was imminent.
If I have to, I will settle on simply never allowing single quotes into the form at all (as it is now), however this may aggravate anyone named say... Bill O'Reilly for the name field, etc., etc.
There are probably other (well I don't know what to call them, 'plug-ins?' 'outside programs?') that would do the job for me, but that is virtually useless since not only do I have no idea how to integrate that, I would have no idea how to tailor it specifically to my web interface/database.
Is there any code that could help me detect a sql injection apostrophe (by the characters surrounding it maybe?) from a normal apostrophe? Keep in mind some of my fields can hold up to 500 characters (textareas) and one up to 1,000.
Thanks for any ideas or help!
No amount of input encoding/cleanup will be as safe as parametrized queries.
See SqlCommand.Parameters for details on parametrized queries.
string commandText = "SELECT * FROM Sales WHERE CustomerID = #ID;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(commandText, connection);
command.Parameters.Add("#ID", SqlDbType.Int);
command.Parameters["#ID"].Value = customerID;
var reader = command.ExecuteReader();
//.....
}
SQL Injections is not a problem with the input containing specific characters, it's a problem with how you handle the input.
By disallowing certain characters you can stop the obvious ways to cause SQL injections, but it's virtually impossible to use that to stop all possible ways.
If encoded correctly, there are no character that causes problems. The best way of doing that for database calls is to use parameterised queries, so that the database driver takes care of encoding the correct characters according to the data type and the specific database.
Also, you need to encode the values correctly when you use them later on, like HTML encoding strings that are put in HTML code, URL encoding strings that are used in an URL (and both for strings that are put in an URL in the HTML code.)
You should use parameterised queries to prevent SQL Injection as other people have already said.
Alexei Levenkov provides a good example of using ADO.NET parameters, but more commonly, you will use the Database Helper when working with WebMatrix Razor pages (ASP.NET Web Pages Framework) where parameter handling is slightly different. The Database.Query method (Query(string commandText, params object[] parameters) takes a string representing the SQL to be executed, and an array of objects, representing the parameter values to be passed to the SQL. The Database helper expects parameter markers to start at #0, and increment by 1 each time e.g.
var sql = "SELECT * From MyTable WHERE TheDate > #0 AND ID > #1";
Then you pass actual values in the following manner:
var data = Database.Open("MyDb").Query(sql, Request["date"], Request["id"]);
Internally, the Database class takes care of matching values to placeholders and creating ADO.NET parameters for you.
I've been looking through the documentation on string formatting in .Net and haven't found this bit yet, and was hoping someone could point me in the right direction. I've come across a working piece of code that takes SqlParameters and puts them in a string like this:
SqlParameter[] arrParams = new SqlParameter[]
{
new SqlParameter("#ClientID", clid),
new SqlParameter("#CustomerID", cuid),
new SqlParameter("#AdminUser", false)
};
string sqlText = string.Format("Insert into [Table1] (RID, CustomerID, AdminUser) values (#ClientID,#CustomerID,#AdminUser)");
..and when running that string down the line in a SqlCommand the proper values are put into the right spots. I'm used to using curly braces for string formatting arguments and not the #symbol and so was wondering where to learn more about this?
This code does not actually need String.Format.
String.Format is for times when you would normally do "string" + variable + "more string". This would be written as String.Format("string{0}morestring", variable); In this case, it is just one string, so that is why there is no need...nothing is being concatenated together.
Here is a good explanation of String.Format
What is happening here is that the #VariableName is being filled with your SqlParameters to avoid SQL Injection. In a nutshell, when you create a SqlParameter, the .NET library looks for either a SQL parameter that matches the name, which could be a stored procedure, function, etc, or any item in a SQL text that begins with # and matches the name.
And here is a good explanation of how SqlParameters work
I think there is some confusion here. The # symbol is used to identify the named SQL parameters within the INSERT INTO statement and not for String.Format.
This is not similar to String.Format what is actually happening is you are running the SQL Command, the command looks for variables called #ClientID,#CustomerID and #AdminUser, which are passed to SQL Server as parameters. You will have something like cmd.Parameters = arrparams a bit further down.
The actual String.Format part around the SQL text is redundant you are right, it will been to use the curly brace route like you suggested.
Hope this helps.
I'm just curious if the IDbCommand.CommandType's value makes a difference in terms of performance?
Basically, I could do either set it as CommandType.Text and pass in a SQL query "EXEC sp_my_stored_procedure ..." as the CommandText. Or, I could set it as CommandType.StoredProcedure and pass in a stored procedure name "my_stored_procedure" as the CommandText.
I'm wondering if there is any performance difference here, or is it just a matter of passing in a query calling the SP vs passing in the name of the SP?
A side note, I realize that it could depend on the driver, but am not sure. So, if that's the case, I'd like to know that as well. Thanks!
Using the CommandType.StoredProcedure is more efficient. The impact would be felt dependent on your app's load.
In my opinion it is also cleaner. I would generally use CommandType.Text if there was a need to build the command at runtime (i.e. SELECT id, name FROM Table WHERE.......).
Hope this helps,
Kevin
I reflected into the Informix object: IfxCommand which has an internal property named AdjustedCommandText. It seems that this method formats the string value to send to the driver based upon the CommandType.
What's interesting is that it either returns one thing for TableDirect or another for StoredProcedure. Or, just the stored text. Now, the StoredProcedure one goes deeper. If there are no command paramters it just returns "EXECUTE PROCEDURE ...", but if there are command parameters involved then it builds the string up starting with "{?=CALL ..." or {CALL ...".
So, I can say as far as Informix is concerned there is no difference except for clarity and cleanliness. As far as other database drivers, I don't haven't looked into them yet.
I been reading a bit about SQL injection and I want to be sure my code is lets say "safe" from it, I was planning on using RegExp validators to check the user input but another post in here suggested only using parametrized querys, well I'm using them but I want to be sure my code is safe, is it?
using ( SqlConnection dataConnection = new SqlConnection(myConnectionString) )
{
using ( SqlCommand dataCommand = dataConnection.CreateCommand() )
{
dataCommand.CommandText = "INSERT INTO Lines (Name, CreationTime) " +
"VALUES (#LineName, #CurrentDateTime)";
dataCommand.Parameters.AddWithValue("#LineName", TextBox2.Text);
dataCommand.Parameters.AddWithValue("#CurrentDateTime", DateTime.Now.ToString());
dataConnection.Open();
//do other DB stuff
I chop the last part to make the post shorter, the rest is just trying and catching exceptions and closing db connection as well as providing user feedback on inserting successful.
Your code is fine, it is protected from injection because the values are passed as parameters not string literals. However, if you are writing this type of data access yourself, have you considered creating SqlParameter objects and explicitly setting the type, size etc, and adding the parameters to the command? AddWithValue will work just fine, but SQL Server will have to determine the type, a little, but unnecessary overhead.
Well, you could always try to inject a SQL statement into the textbox, that will probably give you a quicker, definite answer.
Yes, that's reasonably safe. So long as you don't use "sanitized" variables from a prepared statement to generate dynamic sql later, you're usually ok. The fact that you're using a prepared statement will take care of dealing with escape characters and other simple methods of injection.
I wouldn't forgo any other validation though...
Using C# and System.Data.SqlClient, is there a way to retrieve a list of parameters that belong to a stored procedure on a SQL Server before I actually execute it?
I have an a "multi-environment" scenario where there are multiple versions of the same database schema. Examples of environments might be "Development", "Staging", & "Production". "Development" is going to have one version of the stored procedure and "Staging" is going to have another.
All I want to do is validate that a parameter is going to be there before passing it a value and calling the stored procedure. Avoiding that SqlException rather than having to catch it is a plus for me.
Joshua
You want the SqlCommandBuilder.DeriveParameters(SqlCommand) method. Note that it requires an additional round trip to the database, so it is a somewhat significant performance hit. You should consider caching the results.
An example call:
using (SqlConnection conn = new SqlConnection(CONNSTRING))
using (SqlCommand cmd = new SqlCommand("StoredProc", conn)) {
cmd.CommandType = CommandType.StoredProcedure;
SqlCommandBuilder.DeriveParameters(cmd);
cmd.Parameters["param1"].Value = "12345";
// ....
}
You can use SqlCommandBuilder.DeriveParameters() (see SqlCommandBuilder.DeriveParameters - Get Parameter Information for a Stored Procedure - ADO.NET Tutorials) or there's this way which isn't as elegant.
Although its not exactly what you want, here's some sample code that uses the SqlConnection.GetSchema() method to return all the stored procedures associated with a database, and then subsequently all the parameter names and types for each stored procedure. The example below just loads this into variables. Note that this also returns all the "system" stored procedures, which might not be desirable.
Steve
public void LoadProcedureInfo()
{
SqlConnection connection = new SqlConnection();
ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["ConnectionString"];
connection.ConnectionString = settings.ConnectionString;
connection.Open();
DataTable procedureDataTable = connection.GetSchema("Procedures");
DataColumn procedureDataColumn = procedureDataTable.Columns["ROUTINE_NAME"];
if (procedureDataColumn != null)
{
foreach (DataRow row in procedureDataTable.Rows)
{
String procedureName = row[procedureDataColumn].ToString();
DataTable parmsDataTable = connection.GetSchema("ProcedureParameters", new string[] { null, null, procedureName });
DataColumn parmNameDataColumn = parmsDataTable.Columns["PARAMETER_NAME"];
DataColumn parmTypeDataColumn = parmsDataTable.Columns["DATA_TYPE"];
foreach (DataRow parmRow in parmsDataTable.Rows)
{
string parmName = parmRow[parmNameDataColumn].ToString();
string parmType = parmRow[parmTypeDataColumn].ToString();
}
}
}
}
You can use the SqlCommandBuilder object, and call the DeriveParameters method.
Basically you need to pass it a command, that is setup to call your stored proc, and it will hit the DB to discover the parameters, and create the appropriate parameters in the Parameters property of the SqlCommand
EDIT: You're all too fast!!
SqlCommandBuilder.DeriveParameters(command)
This statement does what I need it to.
Here is a full code sample for the way I solved this problem.
Public Sub GetLogEntriesForApplication(ByVal settings As FilterSettings,
Optional ByVal RowGovernor As Integer = -1)
Dim command As New SqlCommand("GetApplicationActions",
New SqlConnection(m_environment.LoggingDatabaseConnectionString))
Dim adapter As New SqlDataAdapter(command)
Using command.Connection
With command
.Connection.Open()
.CommandType = CommandType.StoredProcedure
SqlCommandBuilder.DeriveParameters(command)
With .Parameters
If settings.FilterOnLoggingLevel Then
If .Contains("#loggingLevel") Then
.Item("#loggingLevel").Value = settings.LoggingLevel
End If
End If
If settings.FilterOnApplicationID Then
If .Contains("#applicationID") Then
.Item("#applicationID").Value = settings.ApplicationID
End If
End If
If settings.FilterOnCreatedDate Then
If .Contains("#startDate") Then
.Item("#startDate").Value = settings.CreatedDate.Ticks
End If
End If
If settings.FilterOnEndDate Then
If .Contains("#endDate") Then
.Item("#endDate").Value = settings.EndDate.Ticks
End If
End If
If settings.FilterOnSuccess Then
If .Contains("#success") Then
.Item("#success").Value = settings.Success
End If
End If
If settings.FilterOnProcess Then
If settings.Process > -1 Then
If .Contains("#process") Then
.Item("#process").Value = settings.Process
End If
End If
End If
If RowGovernor > -1 Then
If .Contains("#topRows") Then
.Item("#topRows").Value = RowGovernor
End If
End If
End With
End With
adapter.TableMappings.Clear()
adapter.TableMappings.Add("Table", "ApplicationActions")
adapter.TableMappings.Add("Table1", "Milestones")
LogEntries.Clear()
Milestones.Clear()
adapter.Fill(m_logEntryData)
End Using
End Sub
Mark has the best implementation of DeriveParameters. As he said, make sure you cache like in this tutorial.
However, I think this is a dangerous way of solving your original problem of database sproc versioning. If you are going to change the signature of a procedure by adding or removing parameters, you should do one of the following:
Code in a backwards-compatible manner by using defaults (for new params) or by simply ignoring a param (for deleted params). This ensures that your client code can always call any version of your stored procedure.
Explicitly version the procedure by name (so you will have my_proc and my_proc_v2). This ensures that your client code and sprocs stay in sync.
Relying on DeriveParameters to validate what version of the sproc you're using seems like the wrong tool for the job, IMHO.
All of these ADO.NET solutions are are asking the code library to query the database's metadata on your behalf. If you are going to take that performance hit anyhow, maybe you should just write some helper functions that call
Select count(*) from information_schema.parameters
where ...(proc name =.. param name=...) (pseudo-code)
Or maybe even generate your parameters based on the param list you get back. This technique will work with multiple versions of MS SQL and sometimes other ANSI SQL databases.
I have been using DeriveParameters with .NET 1.1 and 2.0 since a couple of years now, and worked like a charm every time.
Now I'm working on my first assignment with .NET 3.5, and just found and ugly surprise: DeriveParameters is creating all parameters with SqlDbType "Variant", instead proper SqlDbTypes. This is creating a SqlException when trying to execute SPs with numeric parameters, because SQL Server 2005 says that sql-variant types cant be implictily converted to int (or smallint, or numeric) values.
I just tested the same code with .NET CF 2.0 and SQL Server 2000, and worked as expected, assigning the correct SqlDbType to each parameters.
I had tested .NET 2.0 apps against SQL Server 2005 Databases, so is not a SQL Server related issue, so it has to be something related with .NET 3.5
Any ideas?