I am doing a quick CSHTML page for the purpose of testing.
I need to access database based on the id parameter on the URL:
var id = Request.QueryString["id"];
var db = Database.Open("mydatabase_connection");
var query = "select * from myrecord where id = " + id;
var row = db.QuerySingle(query);
//i am able to display the field (called name) of the selected record in the following way:
#row.name
Obviously, the above approach is subject to security attack. I am hoping to retrieve the record the following way:
var query = "select * from myrecord where id=#0";
var row = db.Execute(query, id);
However, I get runtime error when retrieving the field value:
#row.name
What is the correct way of getting the "row" in the second approach?
Thanks and regards.
Database.Execute is for executing a non-query SQL statement and returns the count of records affected by the SQL statement as an Int.
I think the method you want to use really is Database.QuerySingle, which returns an Object.
ie.
var query = "select * from myrecord where id=#0";
var row = db.QuerySingle(query, id);
Razor:
#row.name
As far as safety from SQL injection goes, this approach is safe. You are passing the URL value into your query as a parameter.
The unsafe way to run the query would be with string concatenation:
var query = "select * from myrecord where id=" + id;
Don't do this! It allows for a malicious user to append SQL statements to your query! Always use parameterized queries instead.
Related
I have a list of items with different ids which represent a SQL table's PK values.
Is there any way to build an efficient and safe statement?
Since now I've always prepared a string representing the statement and build it as I traversed the list via a foreach loop.
Here's an example of what I'm doing:
string update = "UPDATE table SET column = 0 WHERE";
foreach (Line l in list)
{
update += " id = " + l.Id + " OR";
}
// To remove last OR
update.Remove(update.Length - 3);
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
Which feels very unsafe and looks very ugly.
Is there a better way for this?
So yeah, in SQL you've got the 'IN' keyword which allows you to specify a set of values.
This should accomplish what you would like (syntax might be iffy, but the idea is there)
var ids = string.Join(',', list.Select(x => x.Id))
string update = $"UPDATE table SET column = 0 WHERE id IN ({ids})";
MySqlHelper.ExecuteNonQuery("myConnectionString", update);
However, the way you're performing your SQL can be considered dangerous (you should be fine as this just looks like ids from a DB, who knows, better to be safe than sorry). Here you're passing parameters straight into your query string, which is a potential risk to SQL injection which is very dangerous. There are ways around this, and using the inbuilt .NET 'SqlCommand' object
https://www.w3schools.com/sql/sql_injection.asp
https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand?view=dotnet-plat-ext-6.0
It would be more efficient to use IN operator:
string update = "UPDATE table SET column = 0 WHERE id IN (";
foreach (Line l in list)
{
update += l.Id + ",";
}
// To remove last comma
update.Remove(update.Length - 1);
// To insert closing bracket
update += ")";
If using .NET Core Framework, see the following library which creates parameters for a WHERE IN. The library is a port from VB.NET which I wrote in Framework 4.7 years ago. Clone the repository, get SqlCoreUtilityLibrary project for creating statements.
Setup.
public void UpdateExample()
{
var identifiers = new List<int>() { 1, 3,20, 2, 45 };
var (actual, exposed) = DataOperations.UpdateExample(
"UPDATE table SET column = 0 WHERE id IN", identifiers);
Console.WriteLine(actual);
Console.WriteLine(exposed);
}
Just enough code to create the parameterizing SQL statement. Note ActualCommandText method is included for development, not for production as it reveals actual values for parameters.
public static (string actual, string exposed) UpdateExample(string commandText, List<int> identifiers)
{
using var cn = new SqlConnection() { ConnectionString = GetSqlConnection() };
using var cmd = new SqlCommand() { Connection = cn };
cmd.CommandText = SqlWhereInParamBuilder.BuildInClause(commandText + " ({0})", "p", identifiers);
cmd.AddParamsToCommand("p", identifiers);
return (cmd.CommandText, cmd.ActualCommandText());
}
For a real app all code would be done in the method above rather than returning the two strings.
Results
UPDATE table SET column = 0 WHERE id IN (#p0,#p1,#p2,#p3,#p4)
UPDATE table SET column = 0 WHERE id IN (1,3,20,2,45)
I'm working on a WPF application and using SQLite database. I can do every CRUD operation with Entity Framework, but in some specific cases I have to use raw SQL queries, and sometimes it's not returning what I need.
Here is a sample code:
using (var db = new DbContext(AppIO.DatabaseFilePath)) {
var key = 12;
string sql = $"SELECT COUNT(*) FROM SomeTable WHERE SomeField={key}";
var result = db.Database.ExecuteSqlCommand(sql);
}
I simplified the example. Here the result, what I got is -1. I copied the sql string value (after it's built) and executed in SQLiteStuido on the same database and it returned the correct value.
The DatabaseFilePath is correct. The connection is set correctly. I'm checking the same databases (in code and in SQLiteStudio). Any other idea?
Try this:
var result = db.Database.SqlQuery<int>(sql).First();
You have to call SqlQuery method and not ExecuteSqlCommand method. Since SqlQuery returns an IEnumerable you have to call Single. This is a the way to retreive scalar values from a query.
using (var db = new DbContext(AppIO.DatabaseFilePath)) {
var key = 12;
string sql = $"SELECT COUNT(*) FROM SomeTable WHERE SomeField={key}";
var result = db.Database.SqlQuery<int>(sql).Single();
}
I am stuck at one problem and I just can't solve this.
I get this Error:
Error Message
That's the relevant table
The Code:
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = " + #departmentCB.Text;
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#nextMaintainance", nextMaintainanceDT.Value);
command.ExecuteNonQuery();
The weird thing I don't understand is that a similar code works just fine without any error in my project:
query = "UPDATE LDV SET received = #received, department = #department WHERE Id =" + #idTxt.Text;
command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#received", inDT.Value);
command.Parameters.AddWithValue("#department", departmentCb.Text);
command.ExecuteNonQuery();
MessageBox.Show("Lungenautomat wurde aktualisiert");
If relevant, my connection string:
connectionString = ConfigurationManager.ConnectionStrings["SCBA_Manager_0._1.Properties.Settings.SCBAmanagerConnectionString"].ConnectionString;
I really hope you can help me :(
Thank you!
The department column is a text column, so comparing it to a value means the value should be wrapped in quotes.
// This fix is not the recommended approach, see the explanation after this code block
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = '" + departmentCB.Text + "'";
// ^--------------------------^------ single quote added to wrap the value returned by departmentCB.Text
On the other hand, this error does not occur in your second example, because there you're correctly using the Parameters.AddWithValue() method to add the value for the #department parameter, and because id is a numeric column, so it doesn't require the value wrapped in quotes.
However, while the code shown above does the job, it is not the right way of doing the job. The correct way is to used parameters for all values to be injected into a query. The queries you've shown above are already correctly using parameters for some values (e.g. nextMaintenance in the first query, received and department in the second), but are incorrectly doing string concatenation for other values (e.g. department in the first query, id in the second).
Usage of Parameterized SQL
The benefit of using parameterized SQL is that it automatically takes care of adding quotes, prevents SQL injection, etc.
Therefore, its best to change your first code block to:
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = #department";
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("#department", departmentCb.Text);
command.Parameters.AddWithValue("#nextMaintainance", nextMaintainanceDT.Value);
command.ExecuteNonQuery();
Notice how the string query is a single string without any messy concatenation, and that it contains two parameters #nextMaintenance and #department? And how the values for those parameters are correctly injected using Parameters.AddWithValue() in the following lines?
Your second code block can be similarly improved by using a parameter for the Id column.
query = "UPDATE LDV SET received = #received, department = #department WHERE Id = #Id ";
command.Parameters.AddWithValue("#Id", idTxt.Text);
Further Information
Do read up about SQL injection ( https://technet.microsoft.com/en-us/library/ms161953(v=sql.105).aspx ) to see how using string concatenation like your original code can lead to various security issues, and why parameterized queries are the preferred way of injecting dynamic values into SQL queries.
You can read up more about parameterized queries here: https://msdn.microsoft.com/en-us/library/yy6y35y8(v=vs.110).aspx
In your first example, the WHERE clause evaluates to
WHERE department = Kasseedorf
wheras it should be
WHERE department = 'Kasseedorf'
So the line should be
string query = "UPDATE CAC SET nextMaintainance = #nextMaintainance WHERE department = '" + #departmentCB.Text +"'";
It works in the second example, because id is an integer and doesn't neet quotes.
I'm facing a problem in PetaPoco, and I can't figure it out.
I'm using this code :
var db = new OracleConnection(_connectionString);
var query = Sql.Builder;
query.Append("SELECT * FROM City WHERE ID = #0", 1);
return db.Query<City>(query.SQL).ToList();
PetaPoco is not adding the parameter to my sql query.
This is an example from their official website :
var id=123;
var sql=PetaPoco.Sql.Builder
.Append("SELECT * FROM articles")
.Append("WHERE article_id=#0", id);
For you information : I'm using the last version of PetaPoco (5.0.1)
I'd skip Sql.Builder (it's gaining you nothing here), and as a side-note, use db.Fetch as it already returns a List. So:
var query = "SELECT * FROM City WHERE ID = #0";
return db.Fetch<City>(query, 1);
Just query to the Query<> method, not just the sql.
Otherwise you need to pass the Arguments as the second parameter.
Is this query safe against sql injection in combination with Dapper?
If not, what would be the correct way to write it under MySql?
Or is there a better version without using concat?
string sql = "SELECT * from user_profile WHERE FirstName LIKE CONCAT("%",#name,"%");"
var result = connection.query<profile>(sql, new {name});
There isn't a problem with that code, but another approach is to perform the the concat at the caller, i.e.
const string sql = "SELECT * from user_profile WHERE FirstName LIKE #name;";
var result = connection.Query<Profile>(sql, new {name = "%"+name+"%"});
This is safe because you are not building SQL dynamically at all. Name is just a normal parameter. Actually, it has nothing to do with Dapper.
Using a string concat here is the right choice. Alternatively you could use the SUBSTRING_INDEX function.