I am interesting to add parametrize sql queries in my ASP.net application. I have seen some good articles regarding Avoid SQL Injection.
string sql = string.Format("INSERT INTO [UserData] (Username, Password, Role, Membership, DateOfReg) VALUES (#Username, #Password, #Role, #Membership, #DateOfReg)");
SqlCommand cmd = new SqlCommand(sql, conn);
try
{
cmd.Parameters.AddWithValue("Username", usernameTB.Text);
cmd.Parameters.AddWithValue("Password", passwordTB.Text);
cmd.Parameters.AddWithValue("Role", roleTB.Text);
cmd.Parameters.AddWithValue("Membership", membershipTB.Text);
cmd.Parameters.AddWithValue("DateOfReg", dorTB.Text);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
find the Reference
However this way is not useful to me since I couple the DB connection to separate class since I have reuse it.
public class DBconnection{
public int insertQuery(String query) {
int affectedRowCount = 0;
SqlConnection conn = null;
try{
conn = new SqlConnection("Server=localhost;Database=master;UID=sa;PWD=sa;");
SqlCommand cmd = new SqlCommand( query, conn );
cmd.CommandType = CommandType.Text;
conn.Open( );
affectedRowCount = cmd.ExecuteNonQuery( );
conn.Close( );
} catch ( Exception e ){
String error = e.Message;
}
return affectedRowCount;
}
}
Therefore I only use bellow code part to call above class and Insert values to DB.
String SQLQuery1 = insert into Article values('" + Txtname.Text + "','" + TxtNo.Text + "','" + Txtdescription.Text + "' ,0)");
DBconnection dbConn = new DBconnection();
SqlDataReader Dr = dbConn.insertQuery(SQLQuery1);
Please help me to use Parameterize sqlString to Avoid me Sql Injection.
To use #name , # No and #description without use Textbox inputs.
It's perfectly reasonable to do this, but have your class call back (lambda/delegate) out to get the parameters. This is a static method in a class which is called by various overloaded instance methods:
private static int SqlExec(string ConnectionString, string StoredProcName, Action<SqlCommand> AddParameters, Action<SqlCommand> PostExec)
{
int ret;
using (var cn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(StoredProcName, cn))
{
cn.Open();
cmd.CommandType = CommandType.StoredProcedure;
if (AddParameters != null)
{
AddParameters(cmd);
}
ret = cmd.ExecuteNonQuery();
if (PostExec != null)
{
PostExec(cmd);
}
}
return ret;
}
Then, a usage example:
public void Save()
{
Data.Connect().Exec("Project_Update", Cm =>
{
Cm.Parameters.AddWithValue("#ProjectID", ID);
Cm.Parameters.AddWithValue("#PrimaryApplicantID", PrimaryApplicant.IdOrDBNull());
Cm.Parameters.AddWithValue("#SecondaryApplicantID", SecondaryApplicant.IdOrDBNull());
Cm.Parameters.AddWithValue("#ProjectName", ProjectName.ToDBValue());
});
}
It's also possible to do this with non-stored procedure calls.
In your case it would look like:
DBconnection.InsertQuery(
"INSERT INTO [UserData]
(Username, Password, Role, Membership, DateOfReg)
VALUES (#Username, #Password, #Role, #Membership, #DateOfReg)"
,cmd => {
cmd.Parameters.AddWithValue("Username", usernameTB.Text);
cmd.Parameters.AddWithValue("Password", passwordTB.Text);
cmd.Parameters.AddWithValue("Role", roleTB.Text);
cmd.Parameters.AddWithValue("Membership", membershipTB.Text);
cmd.Parameters.AddWithValue("DateOfReg", dorTB.Text);
}
);
Which puts all your database stuff together the way you want and lets the DBconnection keep its internals isolated.
How about instead of a generic InsertQuery() method you write specific InsertQuery methods?
For example:
public void AddNewUser(User u)
{
var query = "insert Users (name, password) values (#0, #1)";
SqlCommand cmd = new SqlCommand(query, conn);
try
{
cmd.Parameters.AddWithValue("#0", u.UserName);
cmd.Parameters.AddWithValue("#1", u.Password);
}
}
This has the advantage of ALL your SQL logic being in this other class, as opposed to the calling class needing to know how to construct the query etc.
It also makes your code more readable, because you see AddUser or UpdateUser or ChangePassword as method calls, and don't have to read SQL at that moment to try and guess what is going on in the program.
HOWEVER if you're going to do something like this, you should check out some MicroORMs, my personal favorite is PetaPoco (or the NuGet version)
PetaPoco and others like Massive and Dapper would let you do something like:
database.Insert(u);
Where u is a User object that maps to your DB's table. It uses ADO.NET and makes sure to use SQL Parameters.
I would suggest using LINQ to SQL, which automatically parametrizes everything.
Q. How is LINQ to SQL protected from SQL-injection attacks?
A. SQL injection has been a significant risk for traditional SQL queries formed by concatenating user input. LINQ to SQL avoids such injection by using SqlParameter in queries. User input is turned into parameter values. This approach prevents malicious commands from being used from customer input.
You can insert, update and delete from a SQL database in a straightforward manner using a DataContext (right-click on your project to add a new item and add the LINQ to SQL Classes template, then use the Server Explorer to add objects to it).
I haven't worked with this in a while, but I believe your code would then look somewhat like this:
UserData user = new UserData();
user.Username = ...;
user.Password = ...;
user.Role = ...;
user.Membership = ...;
user.DateOfReg = ...;
db.UserDatas.InsertOnSubmit(user);
db.SubmitChanges();
When you call SubmitChanges, LINQ to SQL automatically generates and executes the SQL commands that it must have to transmit your changes back to the database.
Edit1:
As an added note, to retrieve an existing item from a database, you could do this:
var user = (from i in db.UserDatas
where i.UserName == "devan"
select i).Single();
Oh, and as is my standard policy when answering questions about databases with login information, I must implore you, for the love of god and all that is holy, to salt and hash your users' passwords.
Related
How would I delete a row from a sql database, either with stored procedures or without, right now I have tried without, using a button press.
This is what I have so far, _memberid has been sent over from a differnt form from the database(For context).
private void btnDelete_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = Lib.SqlConnection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "Delete * From Members where MemberId = " + _memberId;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.DeleteCommand = cmd;
adapter.Fill(MembersDataTable); // Im fairly sure this is incorrect but i used it from old code
DialogResult = DialogResult.OK;
}
If you're trying to do a simple ADO.Net-based delete, then it would be somehting like his:
private void DeleteById(int memberId)
{
// or pull the connString from config somewhere
const string connectionString = "[your connection string]";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = new SqlCommand("DELETE FROM Members WHERE MemberId = #memberId", connection))
{
command.Parameters.AddWithValue("#memberId", memberId);
command.ExecuteNonQuery();
}
}
Use parameter to prevent SQL injection.
There are essentially three main things I'm seeing...
One
You don't need the * in the query. DELETE affects the whole row, so there's no need to specify columns. So just something like:
DELETE FROM SomeTable WHERE SomeColumn = 123
Two
There's no need for a SqlDataAdapter here, all you need to do is execute the query. For example:
cmd.ExecuteNonQuery();
The "non query" is basically a SQL command which doesn't query data for results. Inserts, updates, and deletes are generally "non queries" in this context. What it would return is simply the number of rows affected, which you can use to double-check that it matches what you expect if necessary.
Three
Don't do this:
cmd.CommandText = "Delete From Members where MemberId = " + _memberId;
This kind of string concatenation leads to SQL injection. While it looks intuitively like you're using _memberId as a query value, technically you're using it as executable code. It's less likely (though not impossible) to be a problem for numeric values, but it's a huge problem for string values because it means the user can send you any string and you'll execute it as code.
Instead, use query parameters. For example, you might do something like this:
cmd.CommandText = "Delete From Members where MemberId = #memberId";
cmd.Parameters.Add("#memberId", SqlDbType.Int);
cmd.Parameters["#memberId"].Value = _memberId;
This tells the database engine itself that the value is a value and not part of the executing query, and the database engine knows how to safely handle values.
You could use a DataAdapter, but since you aren't using a datatable, it's just easier to do it without like this:
var sql = "DELETE FROM Members WHERE MemberId=#MemberId";
using(var cmd = new SqlCommand(sql, Lib.SqlConnection))
{
cmd.Connection.Open();
cmd.Parameters.Add("#MemberId",SqlDbType.Int).Value = _memberId;
cmd.ExecuteNonQuery();
}
And if you are using Dapper, you can do this:
Lib.SqlConnection.Execute("DELETE FROM Members WHERE MemberId=#MemberId", new {MemberId=_memberId});
If you are still using DataTables, I would highly recommend you look into using this (or something like this) to simplify your database accesses. It'll make CRUD logic on a database a breeze, and your code will me a lot more maintainable because you can get rid of all the odd needs to do casting, boxing/unboxing, and reduce the chances of runtime bugs because of the use of magic strings that happens so often with DataTables (column names). Once you start working with POCO classes, you'll hate having to use DataTables. That said, there are a few places where DataTables are a better solution (unknown data structures, etc), but those are usually pretty rare.
I'm trying to make a login and I keep getting this error:
System.Data.SqlClient.SqlException: 'Incorrect syntax near 'USERNAME'.'
Here is my code
SqlConnection con = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\Omar\Documents\Data.mdf;Integrated Security=True;Connect Timeout=30");
SqlDataAdapter sda = new SqlDataAdapter("Select Count(*) From [LOGIN] were USERNAME ='" + textBox1.Text +"' and PASSWORD='"+ textBox2.Text +"'",con);
DataTable dt = new DataTable();
sda.Fill(dt);
You're getting voted down not just because this is a duplicate question but because your attempt wraps two security problems in a single piece of code.
So let's take this step by step.
1) Your actual problem is you mispelt WHERE. Your corrected code would look something like
SqlConnection con = new SqlConnection(#"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=C:\Users\Omar\Documents\Data.mdf;Integrated Security=True;Connect Timeout=30");
SqlDataAdapter sda = new SqlDataAdapter("Select Count(*) From [LOGIN] where USERNAME ='" + textBox1.Text +"' and PASSWORD='"+ textBox2.Text +"'",con);
DataTable dt = new DataTable();
sda.Fill(dt);
But you don't need a data adapter or a table, you can return a count from SQL directly. So you could do something like
string sql = "select count(*) from users where username = '" + username + "' and password = '" + password + "'";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand command = new SqlCommand(query, conn))
{
int result = (int)command.ExecuteScalar();
if (result > 0)
{
/// login sucessful
}
}
}
This would work, but you have a security vulnerability called SQL injection.
If we look at a correct login your SQL string would be
select count(*) from login where username = 'alice' and password = 'bob'
This works fine, but if I enter the classic example of SQL injection for a login page as the password, ' OR 1=1 -- then your SQL string becomes this;
select count(*) from login where username = 'alice' and password = '' OR 1=1 --'
This line of SQL will always return 1, because it's highjacked the SQL via SQL injection, adding an OR clause at the end of the statement, OR 1=1 which is always going to be true. It then uses SQL comment syntax to comment out whatever comes after it. So now I can login as anyone at all, even usernames that don't exist.
2) The correct way to build SQL strings, if you don't want to use ORMs that do this for you (and really, use an ORM, all the protection is automatic) is to use a parameterized query, which takes the input and formats it in such a way that any special characters aren't taken as SQL commands, like so
string sql = "select count(*) from login where username = #username and password = #password";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand command = new SqlCommand(query, conn))
{
command.Parameters.Add(new SqlParameter("#username", username));
command.Parameters.Add(new SqlParameter("#password", password));
int result = (int)command.ExecuteScalar();
if (result > 0)
{
/// login sucessful
}
}
}
The parameters are in the SQL query as #parameterName, and then added with command.Parameters.Add(). Now you've avoid SQL injection
3) However this still a security problem. You're storing your passwords in plaintext. If I can gain access to your SQL database (via SQL injection, or you leaving the server open to the world) once I have a copy of the database I have your usernames and passwords and your company is going to end up on haveibeenpwned. You shouldn't be doing this. Passwords should be protected, not via encryption, but via what is called hashing and salting. This transforms the passwords into a value that is derived from it via a one way function, it takes the password, feeds it into some math, and the result out the other side represents the password, but you can't go back the other way, you can't figure out the password from the salted hash. Then when you compare logins, you compute the hash again, and use that in your comparison, in an ORM driven query or a parameterised query.
Now, with all that in mind, I'd strongly advise you to avoid doing any of this yourself. C# & ASP.NET have libraries for usernames and passwords in ASP.NET Identity. I would much rather see you use that, not just because I happen to be the PM owner for that and for .NET Security as a whole, but because it does everything right for you, without you having to do any work.
If this is for a real world application please take some time to go through the OWASP project, it has lots of examples of security problems and details on how to fix them.
Please...
learn about parameters; the code you have is open to SQL injection, which is a huge problem (especially, but not just, on a login form)
learn about not storing plain-text passwords, instead using salted cryptographic hashes
learn about IDisposable; multiple of the objects involved here are disposable and need using
don't use DataTable unless you know why you're using it - there is no reason whatsoever to use it here
learn about tools that will help do all of the above for you
and when, and only when, you have the above "down": fix the typo
Here's a version using "dapper", that covers most of this:
using (var con = SqlConnection(#"...not shown..."))
{
var hash = HashPasswordUsingUsernameAsSalt(
textBox1.Text, textBox2.Text); // impl not shown
int count = con.QuerySingle<int>(#"
select COUNT(1) from [LOGIN]
where [USERNAME]=#cn and [PW_HASH]=#hash",
new { cn = textBox1.Text, hash });
// TODO: do something with count
}
Look like on your 2nd line you have a typo for the WHERE clause.
SqlDataAdapter sda = new SqlDataAdapter("Select Count(*) From [LOGIN] WHERE USERNAME ='" + textBox1.Text +"' and PASSWORD='"+ textBox2.Text +"'",con);
However, you need to learn about SQL injection attacks since your SQL is wide open to hack attempts.
I have a slight issue, I have a ASP.NET Webforms application. I'm sending over a url?id=X were X is my database index or id.
I have a C# class file to run my SQL connection and query. Here is the code:
public DataTable ViewProduct(string id)
{
try
{
string cmdStr = "SELECT * Products WHERE Idx_ProductId = " + id;
DBOps dbops = new DBOps();
DataTable vpTbl = dbops.RetrieveTable(cmdStr, ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString);
return vpTbl;
}
catch (Exception e)
{
return null;
}
}
So as you can see my problem lies within string cmdStr = "SQL Query" + variable;
I'm passing over my index or id through the URL then requesting it and turning it into a string then using ViewProduct(productId).
I don't know what syntax or how to add the id into my C# string sql query. I've tried:
string cmdStr = "SELECT * Products WHERE Idx_ProductId = #0" + id;
string cmdStr = "SELECT * Products WHERE Idx_ProductId = {0}" + id;
also what I have currently to no avail.
I was so sure this would be a duplicate of some canonical question about parameterized queries in C#, but apparently there isn't one (see this)!
You should parameterize your query - if you don't, you run the risk of a malicious piece of code injecting itself into your query. For example, if your current code could run against the database, it would be trivial to make that code do something like this:
// string id = "1 OR 1=1"
"SELECT * Products WHERE Idx_ProductId = 1 OR 1=1" // will return all product rows
// string id = "NULL; SELECT * FROM UserPasswords" - return contents of another table
// string id = "NULL; DROP TABLE Products" - uh oh
// etc....
ADO.NET provides very simple functionality to parameterize your queries, and your DBOps class most assuredly is not using it (you're passing in a built up command string). Instead you should do something like this:
public DataTable ViewProduct(string id)
{
try
{
string connStr = ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
using (SqlCommand cmd = conn.CreateCommand())
{
// #id is very important here!
// this should really be refactored - SELECT * is a bad idea
// someone might add or remove a column you expect, or change the order of columns at some point
cmd.CommandText = "SELECT * Products WHERE Idx_ProductId = #id";
// this will properly escape/prevent malicious versions of id
// use the correct type - if it's int, SqlDbType.Int, etc.
cmd.Parameters.Add("#id", SqlDbType.Varchar).Value = id;
using (SqlDataReader reader = cmd.ExecuteReader())
{
DataTable vpTbl = new DataTable();
vpTbl.Load(reader);
return vpTbl;
}
}
}
}
catch (Exception e)
{
// do some meaningful logging, possibly "throw;" exception - don't just return null!
// callers won't know why null got returned - because there are no rows? because the connection couldn't be made to the database? because of something else?
}
}
Now, if someone tries to pass "NULL; SELECT * FROM SensitiveData", it will be properly parameterized. ADO.NET/Sql Server will convert this to:
DECLARE #id VARCHAR(100) = 'NULL; SELECT * FROM SensitiveData';
SELECT * FROM PRoducts WHERE Idx_ProductId = #id;
which will return no results (unless you have a Idx_ProductId that actually is that string) instead of returning the results of the second SELECT.
Some additional reading:
https://security.stackexchange.com/questions/25684/how-can-i-explain-sql-injection-without-technical-jargon
Difference between Parameters.Add and Parameters.AddWithValue
SQL injection on INSERT
Avoiding SQL injection without parameters
How do I create a parameterized SQL query? Why Should I? (VB.NET)
How can I prevent SQL injection in PHP? (PHP specific, but many helpful points)
Is there a canonical question telling people why they should use SQL parameters?
What type Products.Idx_ProductId is?
Probably it is string, than you need to use quotes: "... = '" + id.Trim() + "'";
Is this code safe from SQL injections? Why?
public void AddPlayer(string username)
{
var query = "INSERT INTO dbo.Player(Username, RegisterDate) VALUES(#Username, #RegisterDate)";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("#Username", username);
command.Parameters.AddWithValue("#RegisterDate", DateTime.Now);
command.Connection.Open();
command.ExecuteNonQuery();
}
}
public DateTime GetRegisterDate(string username)
{
var query = "SELECT RegisterDate FROM dbo.Player WHERE Username = #Username";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("#Username", username);
command.Connection.Open();
return (DateTime)command.ExecuteScalar();
}
}
EDIT: Could injection-safe equivalent code be written using a stored procedure? If so, what the stored procedure would be like?
Yes, It looks safe.
Because it uses parameters.
You run a risk of SQL-injection when you create queries like
baseQueryText + " WHERE Username =" + TextBox.Text;
Reguarding the Edit: When you use a Stored Procedure you always use parameters so they are safe too. No special effort required, but you still could/should filter incoming data.
Yes. You are using parameterized queries, which are in general considered safe from SQL injection.
You may still want to consider filtering your inputs anyway.
Yes, all the non-static data is being fed in via bound parameters.
I'm using Visual C# connected to MySQL for study purposes and I'm stuck in throwing an error to the user when he types a username that already exists.
Current code to put things into the database (it may be useless, once my question may be much more about SQL):
s = new sql(); // This calls a class that works as an adapter to connect form with the database
Conn = s.Connection;
Conn.Open();
coma = Conn.CreateCommand();
coma.CommandText = "INSERT INTO test.test (`user`,`password`) VALUES ('"+username.Text+"','"+password.Text+"');";
coma.ExecuteNonQuery();
What I want to do it compare "username.Text" ("username" is a TextBox) with the values on database's "test" table and, if some value match, evoke a MessageBox.Show("Hey guy, this username is already in use! Try something different)
Some points about your code sample
You want to be sure that you dispose of your connection and command objects. For my answer, I've wrapped them in using statements which will take care of that for me.
You do not want to go to the database with unsanitized inputs. I am going to use parameterized queries in the example.
It's not a good idea to store passwords in plain text. I am not going to demonstrate more secure techniques, just know to look for information about encrypting passwords, salt keys, etc.
And now for some code. In this, I'm using OleDb objects, retrofit to your particular database. And, of course, provide appropriate names to tables, columns, etc.
using (OleDbConnection connection = SomeMethodReturningConnection())
using (OleDbCommand command = SomeMethodReturningCommand())
{
command.Parameters.Add(new OleDbParameter("#username", username));
command.CommandText = "Select Count(*) From Users where Username = #username";
connection.Open();
int output = (int)command.ExecuteScalar();
if (output > 0)
{
// username already exists, provide appropriate action
}
else
{
// perform insert
// note: #username parameter already exists, do not need to add again
command.Parameters.Add(new OleDbParameter("#password", password));
command.CommandText = "Insert Into Users (Username, Password) Values (#username, #password)";
command.ExecuteNonQuery();
}
}
Thank you Anthony! Your answer put me on the right track. Although there is something that the people who will read this post should change from your code in order to get it working with Odbc connectors: the way as parameters are parsed and the way as the textbox content is extracted:
using (OdbcConnection connection = SomeMethodReturningConnection())
using (OdbcCommand command = SomeMethodReturningCommand())
{
command.Parameters.Add(new OdbcParameter("#username", username.Text));
command.CommandText = "Select Count(*) From Users where Username = ?";
connection.Open();
int output = (int)command.ExecuteScalar();
if (output > 0)
{
// username already exists, provide appropriate action
}
else
{
// perform insert
// note: #username parameter already exists, do not need to add again
command.Parameters.Add(new OdbcParameter("#password", password.Text));
command.CommandText = "Insert Into Users (Username, Password) Values (?,?)**";
command.ExecuteNonQuery();
}
}
Thank you anyway!