How to prevent SQL Injection in this code? - c#

How can i prevent these code of getting SQL injected? It's a login system that i'm learning. Here's the code!
if (!(string.IsNullOrWhiteSpace(textBox1.Text)) && !(string.IsNullOrWhiteSpace(textBox2.Text)))
{
MySqlConnection mcon = new MySqlConnection("datasource = 127.0.0.1; port = 3306; username = root; password = ; database = rpgmaster;");
mcon.Open();
DataTable table = new DataTable();
MySqlDataAdapter adapter = new MySqlDataAdapter("Select * From users where Username = '" + textBox2.Text + "' and password = '" + textBox1.Text + "'", mcon);
adapter.Fill(table);
if (table.Rows.Count <= 0)
{
MessageBox.Show("Você não está registrado!");
}
else
{
MessageBox.Show("Logado com sucesso! ");
}
mcon.Close();
}
Thanks for the help! Really appreciate it!

If you're learning, you could perhaps move on from this old low level way of doing data access and use something a bit more modern and easy. Dapper is an example of a library that isn't a huge leap above what you already know but makes your life a lot nicer:
using(var conn = new MySqlConnection("conn str here"){
var sql = "SELECT count(*) FROM tblUsers WHERE username = #u AND password = #p";
var prm = new {
u = txtUsername.Text, //give your textboxes better names than textbox2,textbox1!
p = txtPassword.Text.GetHashCode() //do NOT store plain text passwords!
};
bool valid = await conn.QuerySingleAsync<int>(sql, prm) > 0;
if(valid)
... valid login code
else
... invalid login
}
Some notes on this:
dapper is a device that you simply give your sql and parameter values to
the sql holds #parameters names like #u
an anonymous typed object has properties called the same name as the parameter name, with a value, like u = "my username"
use async/await when running queries; dapper makes this easy. Avoid jamming your UI up on queries that take 10 seconds to run
in this case you only need to ask the db to count the matching records, you don't need to download them all to find out if there are any, so we use QuerySingleAsync<int> which queries a single value of type it, and if it's more than 0, the login was valid
never store password in a database in plaintext. Use a one way hashing function like MD5, SHA256 etc, even the lowly string.GetHashCode is better than storing plaintext, particularly because people use the same passwords all the time so anyone breaking into your db (very easy; the password is in the code) will reveal passwords treat people probably use in their banking etc. We can't really be asking, on the one hand, how to prevent a huge security hole like SQL injection, and then on the other hand leave a huge security hole like plaintext passwords ;)
always name your textboxes a better name than the default textboxX - it takes seconds and makes your code understandable. If Microsoft called all their class property names like that, then the entire framework would be full of things like myString.Int1 rather than myString.Length and it would be completely unusable
life is too short to spend it writing AddWithValue statements; use Dapper, Entity Framework, strongly typed datasets.. Some db management technology that eases the burden of writing that code
Where Dapper makes things really nice for you is its ability to turn objects into queries and vice versa; this above is just a basic count example, but suppose you had a User class:
class User
{
string Name { get; set; }
string HashedPassword { get; set; }
int age {get; set; }
}
And you had a table tblUsers that was similar (column names the same as the property names), then you could query like:
User u = new User() { Name = "someuser" };
User t = await conn.QuerySingleAsync<User>("SELECT Name, HashedPassword, Age FROM tblUsers WHERE Name = #Name", u);
We want to look up all the info of the someuser user, so we make a new User with that Name set (we could also use anonymous type, like the previous example) and nothing else, and we pass that as the parameters argument. Dapper will see the query contains #Name, pull the contents of the Name from the u user that we passed in, and run the query. When the results return it will create a User instance for us, fully populated with all the data from the query
To do this old way we'd have to:
have a command,
have a connection,
add parameters and values,
open the connection,
run the sql,
get a reader,
check if the reader had rows,
loop over the reader pulling the first row,
make a new User,
use reader.GetInt/GetString etc to pull the column values out one by one and
finally return the new user
oh and dispose of all the db stuff, close the connection etc
Writing that code is repetitive, and it is really boring. In computing, when we have something repetitive and boring, that we need to do thousands of times through out life (like serializing to json, calling a webservice, designing a windows UI) we find some way to make the computer do the repetitive boring bit; they do it faster and more accurately than we can. This is exactly what Dapper does; it does away with that boring repetitive and reduces it to a single line where you say what you want back, using what query, with what parameters. And it keeps your UI working:
await x.QueryAsync<type>(query, parameters)
Win. Seek out some Dapper tutorials! (I have no affiliation)

Try using parameters please see updated sample of your code below:
if (!(string.IsNullOrWhiteSpace(textBox1.Text)) && !(string.IsNullOrWhiteSpace(textBox2.Text)))
{
using (MySqlConnection mcon = new MySqlConnection("datasource = 127.0.0.1; port = 3306; username = root; password = ; database = rpgmaster;"))
{
mcon.Open();
MySqlCommand cmd = new MySqlCommand("Select * from users where username=?username and password=?password", mcon);
cmd.Parameters.Add(new MySqlParameter("username", textBox2.Text));
cmd.Parameters.Add(new MySqlParameter("password", textBox1.Text));
MySqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows == true)
{
MessageBox.Show("Você não está registrado!");
}
else
{
MessageBox.Show("Logado com sucesso! ");
}
}
}

Use parameters to pass and check their length, Use stored procedure instead of a query in the code. Use columns instead of * in Select. And please make sure you don't store the plain password in the DB

Use Parameters
using (MySqlConnection mcon = new MySqlConnection(connectionString))
{
string commandText = "SELECT * FROM users WHERE Username = '#tbxText'"
SqlCommand command = new SqlCommand(commandText, mcon);
command.Parameters.AddWithValue("#tbxText", textBox2.Text);
}

Related

Additional information: An expression of non-boolean type specified in a context where a condition is expected, near :

guys i'm trying to execute this Multi login page and in c# wpf i'm a beginner in programming and i followed each step from youtube channels and couldn't make it work any help !!
private void button_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(#"Data Source=root**strong text**\SQLEXPRESS;Initial Catalog=Log-In;Integrated Security=True");
SqlCommand cmd = new SqlCommand("select * from Login where username"+txt_usr.Text+"password"+txt_pass.Text+"", con);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
sda.Fill(dt);
string cmbitemvalue = comboBox1.SelectedItem.ToString();
if (dt.Rows.Count > 0)
{
for(int i = 0; i<dt.Rows.Count; i++)
{
if(dt.Rows[i]["usertype"].ToString()==cmbitemvalue)
{
MessageBox.Show("you are login as " + dt.Rows[i][2]);
if (comboBox1.SelectedIndex == 0)
{
Admin aa = new Admin();
aa.Show();
this.Hide();
}else
{
Student ss = new Student();
ss.Show();
this.Hide();
}
}
}
}
}
}
}
Your SQL Query you are creating looks invalid
Let's focus on the username part.
I can imagine that txt_usr.Text contains the username, let's say leo. The way you wrote it, this would give :
"select * from Login where username"+txt_usr.Text
which is :
select * from Login where usernameleo
(hence, the cause of the error, there is no condition here, just a name of an invalid column)
The correct query looks like this :
select * from Login where username='leo'
or this
select * from Login where username="leo"
depending on the database kind you use (SQL Server, SQLite, MYSQL, Postgresql, etc....).
If you want 2 conditions, the proper syntax is (usually) to use AND
select * from Login where username="leo" and password='secret'
So, you are missing an equal sign, the boolean AND, and proper quotation.
That's not all.
!!! Your SQL query is vulnerable to SQL injection, use .NET SQL Parameters !!!
illustration : https://xkcd.com/327/
Instead of using whatever the user enters in the text box (including potential malicious code) the correct way to do it in C# is to use SQL parameters.
These will take care of sanitizing the input for you, and it will also be clearer in the code, and you don't need to worry about quotation :
SqlCommand cmd = new SqlCommand("select * from Login where username=#username AND password=#password", con);
cmd.AddParameter('#username', txt_usr.Text);
cmd.AddParameter('#password', txt_pass.Text);
Even if this is homework, we have all seen too many websites / companies hacked and data destroyed or stolen so easily because this kind of vulnerability has been made possible, I think most db and security experts here are constantly stressing that you need to learn this fast, and never use unsanitized input for sql queries, ever.
Other security consideration
The fact that the password seem to be in clear text in the database is also a source of concern, please search about "clear text password" and "properly hash and salt passwords" to learn more about this.
But this a bit more work to understand than the SQL parameters, and a few lines of additional lines of code in your program.

C# cannot convert database value to string

I am working on a school project and am having trouble converting a piece of data from a Access database into a string that I can pass to a second form in C#. I know the connection to the database is working and that I am referencing the right table in it to get the information, so I'm not sure what I'm doing wrong. It doesn't show any errors in the code, but every time I run the application, it crashes because it can't find a value from the database for the string at the string accountnumber = reader["Account_Number"].ToString(); line. Is there something I'm doing wrong?
OleDbCommand command = new OleDbCommand();
command.Connection = connection;
command.CommandText = "select * from User_Info where Username='" +txt_Username.Text+ "' and Password='" +txt_Password.Text+ "'";
OleDbDataReader reader = command.ExecuteReader();
int count = 0;
string accountnumber = reader["Account_Number"].ToString();
while (reader.Read())
{
count = count+1;
}
if (count == 1)
{
MessageBox.Show("Login Successful!", "Success!");
connection.Close();
connection.Dispose();
this.Hide();
User_Account_Screen UAS = new User_Account_Screen();
UAS.Number = accountnumber;
UAS.ShowDialog();
Try not to reuse connections unless you have to. The main reason is that you generally want to close connections as soon as possible and it will guard you against possible race conditions later if you have multiple events/actions that can occur at the same time that are data driven.
Close your connections as soon as possible so you do not have open external resources.
To ensure that 1 and 2 occur wrap your IDisposable types in a using block.
Always use parameters instead of string concatenation in your queries. It guards against sql injection (not applicable to MS Access) and ensures you never has issues with strings that contain escape charaters.
A note about MS Access and parameters: Place holders are usually specified by the ? character and are position dependent. This is critical, you cannot rely on the name of the parameter. If you have a parameter collection with 3 parameters in that collection then those parameters must appear in the same order in the query.
I notice you probably have password as plain text, never store passwords in plain text! Instead store a hash of the password. To see if the password matches the user's supplied password at login hash that input as well and then compare that hash to the stored hash to see if they are the same. Use a secure password hashing algorithm like pbkdf2, scrypt, or bcrypt.
To see if a row exists or to just return a single value you can also use ExecuteScalar with a null check as it will return null if no records are returned. I altered the code to just return accountnumber using ExecuteScalar. I also enclosed the column name in brackets which is good practice when including characters outside the range of a-z and 0-9 in your column name.
If you were to want to return data and read it using a data reader then do not use * for your return. Specify your column names instead. This will guard your code against schema changes like columns being added or column order changes.
Here is the updated code.
string accountnumber = null;
using(OleDbConnection connection = new OleDbConnection(/*add your connection string here*/))
using(OleDbCommand command = new OleDbCommand("select [Account_Number] from User_Info where Username = ? AND Password = ?", connection))
{
command.Parameters.Add(new OleDbParameter("#username", OleDbType.VarChar)).Value = txt_Username.Text;
command.Parameters.Add(new OleDbParameter("#password", OleDbType.VarChar)).Value = txt_Password.Text;
connection.Open();
accountnumber = command.ExecuteScalar() as string;
}
if (accountnumber != null)
{
MessageBox.Show("Login Successful!", "Success!");
this.Hide();
User_Account_Screen UAS = new User_Account_Screen();
UAS.Number = accountnumber;
UAS.ShowDialog();
}

Checking and Saving/Loading from MySQL C#

I am making something that requires MySQL. I have the saving done from in-game, which is simply done by INSERT.
I have a column that will have a password in and I need to check if the inputted password matched any of the rows and then if it is, get all of the contents of the row then save it to variables.
Does anyone have an idea how to do this in C#?
//////////////////////////
I have found how to save and get the string, however it will only get 1 string at a time :(
MySql.Data.MySqlClient.MySqlCommand command = conn.CreateCommand();
command.CommandText = "SELECT * FROM (player) WHERE (pass)";
command.ExecuteNonQuery();
command.CommandType = System.Data.CommandType.Text;
MySql.Data.MySqlClient.MySqlDataReader reader = command.ExecuteReader();
reader.Read();
ayy = reader.GetString(1);
print (ayy);
if(ayy == password){
//something
}
My best practice is to use MySQLDataAdapter to fill a DataTable. You can then iterate through the rows and try to match the password.
Something like this;
DataTable dt = new DataTable();
using(MySQLDataAdapter adapter = new MySQLDataAdaper(query, connection))
{
adapter.Fill(dt);
}
foreach(DataRow row in dt.Rows)
{
//Supposing you stored your password in a stringfield in your database
if((row.Field<String>("columnName").Equals("password"))
{
//Do something with it
}
}
I hope this compiles since I typed this from my phone. You can find a nice explanation and example here.
However, if you are needing data from a specific user, why not specificly ask it from the database? Your query would be like;
SELECT * FROM usercolumn WHERE user_id = input_id AND pass = input_pass
Since I suppose every user is unique, you will now get the data from the specific user, meaning you should not have to check for passwords anymore.
For the SQL statement, you should be able to search your database as follows and get only the entry you need back from it.
"SELECT * FROM table_name WHERE column_name LIKE input_string"
If input_string contains any of the special characters for SQL string comparison (% and _, I believe) you'll just have to escape them which can be done quite simply with regex. As I said in the comments, it's been a while since I've done SQL, but there's plenty of resources online for perfecting that query.
This should then return the entire row, and if I'm thinking correctly you should be able to then put the entire row into an array of objects all at once, or simply read them string by string and convert to values as needed using one of the Convert methods, as found here: http://msdn.microsoft.com/en-us/library/system.convert(v=vs.110).aspx
Edit as per Prix's comment: Data entered into the MySQL table should not need conversion.
Example to get an integer:
string x = [...];
[...]
var y = Convert.ToInt32(x);
If you're able to get them into object arrays, that works as well.
object[] obj = [...];
[...]
var x0 = Convert.To[...](obj[0]);
var x1 = Convert.To[...](obj[1]);
Etcetera.

Retrieve single value from Database, much like DLookup() in MS Access

I am exploring Silverlight (C#) and SQLServer as a next evolution for our current (slow) Access database. So far everything has been great, using DomainServices to retrieve the data I need. In our database we have a table (Supervisors) with Supervisor_ID, Supervisor_FirstName, Supervisor_LastName and many other fields.
What I want to do is recreate a function I use in my current database called EntityNameFirstLast(EntityID) which would take an integer. I could then retrieve the value of [Supervisor_FirstName] from [Supervisors] table where [Supervisor_ID] == EntityID using the following:
FirstName = DLookup("[Supervisor_FirstName]", "Supervisors", "[Supervisor_ID] = EntityID
I would do the same for lastname and combine the strings returning one string with First and last name.
How can I get just a single value from my database through my DomainService (or any way for that matter)? I understand that IQueryable GetSupervisorByID(Int SupID) will return the entire row that I need, but how can I get a specific field from that row?
I am also aware that I can set the DomainDataSource in my XAML and then bind to the data I want, but I am curious if what I asked above is doable or not.
There are number of ways you can accomplish your requirement if what you need is a single value from MS-SQL server:
1.Use a Query to do the concatenation and then use its output in your code
Select Supervisor_FirstName + ' ' + Supervisor_LastName as Supervisor_FullName From Supervisors Where Supervisor_ID = EntityID
Now you can get the above query to execute through a SqlCommand and get the part thats interesting to you
private string GetSupervisorFullName(string entityID, string connectionString) {
string query = "Select Supervisor_FirstName + ' ' + Supervisor_LastName as Supervisor_FullName From Supervisors Where Supervisor_ID = #EntityID";
string supervisorFullname = "";
using(SqlConnection con = new SqlConnection(connectionString)) {
SqlCommand cmdSupervisorFullname = new SqlCommand();
cmdSupervisorFullname.Connection = con;
cmdSupervisorFullname.CommandText = query;
cmdSupervisorFullname.CommandType = CommandType.Text;
SqlParameter paraEntityID = new SqlParameter();
paraEntityID.ParameterName = "#EntityID";
paraEntityID.SqlDbType = SqlDbType.NVarChar;
paraEntityID.Direction = ParameterDirection.Input;
paraEntityID.Value = entityID;
cmdSupervisorFullname.Parameters.Add(paraEntityID);
try {
con.Open();
supervisorFullname = (String) cmdSupervisorFullname.ExecuteScalar();
} catch(Exception ex) {
Console.WriteLine(ex.Message);
}
return supervisorFullname;
}
}
2.Second way would be create a Scalar function in the SQL for your requirement and then access that function using the same kind of method as mentioned above.
Then finally you would take the return value from your method GetSupervisorFullName and populate any control value of your choice.
Please do note that there are again other methods of doing the same with LINQtoSQL or with any other ORM tools. The above 2 methods are the basic way of accomplishing them.
Hope that helps.

Getting column info from db with C#

In PHP I am able to retrieve information from a db like so:
<?php
$sql = "SELECT * FROM users";
$result = mysql_query($sql);
$data = array();
while($row = mysql_fetch_assoc($result))
{
$data[] = $row;
}
I am trying to acomplish the same thing in C#:
OdbcCommand cmd = new OdbcCommand("SELECT * FROM users WHERE id = #id");
cmd.Parameters.Add("#id", id);
OdbcDataReader reader = cmd.ExecuteReader();
Dictionary<string, string> data = new Dictionary<string, string>();
while (reader.Read())
{
data.Add("id", reader.GetString(0));
data.Add("username", reader.GetString(1));
data.Add("firstName", reader.GetString(2));
}
return data;
Is it possible to reference the column in the table by name without going through all this trouble?
You can use the OdbcDataAdapter class to populate a DataSet, which would probably be a bit simpler.
Yes, but it is SLOW. Not slower than your approach, granted, but slow - I would mark your whole code for a review...
Dont ask for "*", ask for the fields. Good SQL practice - and as you know the fields, guess what, you dont ahve to guess them. On top, you assume id is fiel 0, username field 1...
If that is not possible - read the docomentation. There is a "GetOrdinal" method that takes a field name and.... returns.... the field index.
That said, this whole code is fully redundant. Have a look at proper data access layers, or at least BLTOOLKIT - you can move all that code into ONE abstract method with the rest being automatically generated.

Categories

Resources