SQL selecting multiple columns at once - c#

Hallo I am still new in this game. I am struggling to select the username and password from the database to verify it.
What this below mentioned code can do is ONLY select the username.
I need help to modify the select statement to select both username and password.
public override int SelectUser(string username, ref User user)
{
int rc = 0;
try
{
_sqlCon = new SQLiteConnection(_conStr);
bool bRead = false;
user = new User();
_sqlCon.Open();
// This selection string is where I am struggling.
string selectQuery = "SELECT * FROM Users WHERE [uUsername] = ' " + username + " ' ";
SQLiteCommand sqlCmd = new SQLiteCommand(selectQuery, _sqlCon);
SQLiteDataReader dataReader = sqlCmd.ExecuteReader();
bRead = dataReader.Read();
if(bRead == true)
{
user.Username = Convert.ToString(dataReader["uUsername"]);
user.Password = Convert.ToString(dataReader["uPW"]);
rc = 0;
}// end if
else
{
rc = -1;
}// end else
dataReader.Close();
}// end try
catch(Exception ex)
{
throw ex;
}// end catch
finally
{
_sqlCon.Close();
}// end finally
return rc;
}// end method

You add the AND logical operator to the WHERE first condition to get a double condition. However, this whole approach using string concatenations is wrong.
It is a well known source of bugs and a big security risk called Sql Injection
Instead you use a parameterized query like this
string selectQuery = #"SELECT * FROM Users
WHERE [uUsername] = #name AND
[uPw] = #pass";
SQLiteCommand sqlCmd = new SQLiteCommand(selectQuery, _sqlCon);
sqlCmd.Parameters.Add("#name", DBType.String).Value = username;
sqlCmd.Parameters.Add("#pass", DBType.String).Value = password;
SQLiteDataReader dataReader = sqlCmd.ExecuteReader();
bRead = dataReader.Read();
....
I assume that you have in the variable password the value to search for, change it to your actual situation.
Consider also that storing passwords in clear text inside a database is another security risk to avoid. The question Best way to store password in a database explains in great detail the reasons and the correct way to do it

Related

WHERE Name='{NameInput.Text}' AND Password='{GetHashString(PasswordInput.Text)} not working

I'm trying to get my LoginButton to work, it isn't really doing what I want it to do.
I already have a RegisterButton which works perfectly and creates the account without any problems, but when trying to do my LoginButton it connects to the database but doesn't really check if the account exists using selectQuery and it should change WarningLabel.Text to "Wrong Name or Password". it does go through the first try and changes the WarningLabel.Text to "Welcome " + NameInput.Text;
private void LoginButton_Click(object sender, System.EventArgs e)
{
string selectQuery = $"SELECT * FROM bank.user WHERE Name='{NameInput.Text}' AND Password='{GetHashString(PasswordInput.Text)}';";
MySqlCommand cmd;
connection.Open();
cmd = new MySqlCommand(selectQuery, connection);
try
{
cmd.ExecuteNonQuery();
WarningLabel.Text = "Welcome " + NameInput.Text;
} catch
{
WarningLabel.Text = "Wrong Name or Password";
}
connection.Close();
}
Best Regards - Nebula.exe
The ExecuteNonQuery is not intented to be used with SQL statements that return data, you should use ExecuteReader or ExecuteScalar, you can check the MySqlCommand.ExecuteReader documentation
Warning: Your code does have a SQL Injection vulnerability in this part of the SQL statement Name='{NameInput.Text}' Check this SQL Injection explanation
Usage example (from the documentation, slightly modified):
using (MySqlConnection myConnection = new MySqlConnection(connStr))
{
using (MySqlCommand myCommand = new MySqlCommand(mySelectQuery, myConnection))
{
myConnection.Open();
MySqlDataReader myReader = myCommand.ExecuteReader();
while (myReader.Read())
{
Console.WriteLine(myReader.GetString(0));
}
}
}
You should check if there are records returned. cmd.ExecuteNonQuery(); won't tell you if records are returned because it will just execute the query. You should use ExecuteScalar or a MySQL Data Reader ExecuteReader and track the results.
Note : Your code is prone to SQL Injections, you might want to use Parameters in your query like #name and #password.
Your Query goes something like this.
string selectQuery = $"SELECT IFNULL(COUNT(*),0) FROM bank.user WHERE Name=#name AND Password=#password;";
Then use parameters
cmd.parameters.AddWithValue(#name, NameInput.Text);
cmd.parameters.AddWithValue(#password, GetHashString(PasswordInput.Text));
Then verify if the query returns result
If cmd.ExecuteScalar() > 0
//If count is > 0 then Welcome
//Else Wrong username or password
End If
Your life, made easy:
private void LoginButton_Click(object sender, System.EventArgs e)
{
var cmd = "SELECT * FROM bank.user WHERE Name=#name AND Password=#pw";
using var da = new MySqlDataAdapter(cmd, connection);
da.SelectCommand.Parameters.AddWithValue("#name", NameInput.Text);
da.SelectCommand.Parameters.AddWithValue("#pw",GetHashString(PasswordInput.Text));
var dt = new DataTable();
da.Fill(dt);
if(dt.Rows.Count == 0)
WarningLabel.Text = "Wrong Name or Password";
else
WarningLabel.Text = $"Welcome {dt.Rows[0]["FullName"]}, your last login was at {dt.Rows[0]["LastLoginDate"]}";
}
Your life, made easier (with Dapper):
class User{
public string Name {get;set;} //username e.g. fluffybunny666
public string FullName {get;set;} //like John Smith
public string Password {get;set;} //hashed
public DateTime LastLoginDate {get;set;}
}
//or you could use a record for less finger wear
record User(string Name, string FullName, string Password, DateTime LastLoginDate);
...
using var c = new MySqlConnection(connection):
var u = await c.QuerySingleOrDefaultAsync(
"SELECT * FROM bank.user WHERE Name=#N AND Password=#P",
new { N = NameInput.Text, P = GetHashString(PasswordInput.Text)}
);
if(u == default)
WarningLabel.Text = "Wrong Name or Password";
else
WarningLabel.Text = $"Welcome {u.FullName}, your last login was at u.LastLoginDate";

(Sqlite) How would I get the if statement to check all lines of my database?

Im making a simple login system, currently i am making a method which checks if the username entered already exists in the database if it does it returns true else it will return false. The code i have tried at the moment only checks the first line in the database and if i try entering below the first line that already exists in the database it will still return false. What can i do to fix this
{
string conn = "URI=file:" + Application.dataPath + "/logindb.db";
IDbConnection dbconn;
dbconn = (IDbConnection) new SqliteConnection(conn);
dbconn.Open();
IDbCommand dbcmd = dbconn.CreateCommand();
string sqlQuery = "SELECT id,username, password " + "FROM user";
dbcmd.CommandText = sqlQuery;
IDataReader reader = dbcmd.ExecuteReader();
while (reader.Read())
{
int id = reader.GetInt32(0);
string usernames = reader.GetString(1);
string passwords = reader.GetString(2);
if(Username == usernames )
{
return true;
}
else
{
return false;
}
} ```
Your code is looking for an exact match. Keep in mind that "Stan" does not equal "stan" nor does "Stan" = "Stan "
I agree, however, with Neil, in that you always want to limit your searches if possible using the WHERE clause. It doesn't matter if you have 5 or 5000 records, that's just best practice.
If you find a match, set a bool to true and break out of the loop, then return the bool. If there are no matches then it will implicitly return false.
do not use this as a model of a good authentication system, it is highly flawed
bool found = false;
while (reader.Read())
{
int id = reader.GetInt32(0);
string usernames = reader.GetString(1);
string passwords = reader.GetString(2);
if(Username == usernames )
{
found = true;
break;
}
}
return found;

Check login credentials from a database

I am storing usernames and passwords in a MySql database. I am using the following code to verify the credentials based on the data from my database for a login. The codes works fine. My question is whether this is bad practice and is there a better way to do this.
My approach is to connect to that database, extract and store those information in a List and compare them to the users input coming from a text box input.
//Extracting information from the database and storing it in a List
public void Login()
{
MySqlCommand cmdReader;
MySqlDataReader myReader;
userQuery = "SELECT * FROM User";
string name = "Name";
string user = "UserName";
string pw = "Password";
string connString = "server=" + server + "; userid=" + userid + "; password=" + password + "; database=" + database;
try
{
conn.ConnectionString = connString;
conn.Open();
cmdReader = new MySqlCommand(userQuery, conn);
myReader = cmdReader.ExecuteReader();
while (myReader.Read())
{
string tempUser, tempPassword;
if (name != null)
{
tempUser = myReader.GetString(user);
tempPassword = myReader.GetString(pw);
users.Add(tempUser);
passwords.Add(tempPassword);
}
}
myReader.Close();
}
catch (Exception err)
{
MessageBox.Show("Not connected to server. \nTry again later.");
Application.Current.Shutdown();
}
}
//Comparing the List data with the users input from textbox1 and textbox2 to verify
private void btn1_Click(object sender, RoutedEventArgs e)
{
for (int x = 0; x < users.Count; x++)
{
for (int y = 0; y < passwords.Count; y++)
{
if (users[x] == textbox1.Text && passwords[y] == textbox2.Text)
{
MessageBox.Show("Login successful");
}
}
}
}
Do not store password in plain text in database, store their hashes. See(Best way to store password in database)
Instead of querying and retrieving all the users, send specific user name and password to database and compare the returned result.
As a side note, do not use string concatenation to form SQL queries, instead use parameters, something like:
using (MySqlCommand cmd = new MySqlCommand("SELECT Count(*) FROM User = #userName AND password = #password"),conn)
{
cmd.Parameters.AddwithValue("#username", username);
cmd.Parameters.AddwithValue("#password", password);
....
var count = cmd.ExecuteScalar(); //and check the returned value
}
Currently you are retrieving all records from User table and then comparing it with the values at client side, imagine if you have a large number of users, brining that much data to client end would not make sense.
Looping on two list and comparing index is not a optimal.
If you want to pre fetch then a Dictionary.
Key lookup on Dictionary is O(1)
Dictionary<string,string> UserIDpw = new Dictionary<string,string>();
while (myReader.Read())
{
UserIDpw.Add(myReader.GetString(0), myReader.GetString(1));
}
But the answer from Habib is a better approach. You don't have a performance issue the requires you to prefetch and prefetch comes with issues. For one you have the passwords on the web server where thy are easier to hack.
I would use a stored procedure for this, and then send username and password as parameters. Depending on whether this is intranet app, or something that is out on the internet, I might do the hash thing as Habib suggests.

Update Database error

I have a form which displays selected datagridviewrow data in textboxes. I would like to edit and update the data from this form and update and save into the datatable when the user clicks update.
When I click update I get an error message:
There was an error parsing the query. [ Token line number = 1,Token line offset = 25,Token in error = ( ]
private void editBTN_Click(object sender, EventArgs e)
{
bool notEditable = true;
if (editBTN.Text == "Update")
{
UpdateDataBase();
editBTN.Text = "Edit";
deleteBTN.Visible = true;
notEditable = true;
}
else
{
deleteBTN.Visible = false;
editBTN.Text = "Update";
deleteBTN.Visible = false;
notEditable = false;
}
firstTxt.ReadOnly = notEditable;
surenameTxt.ReadOnly = notEditable;
address1Txt.ReadOnly = notEditable;
address2Txt.ReadOnly = notEditable;
countyTxt.ReadOnly = notEditable;
contactTxt.ReadOnly = notEditable;
emailTxt.ReadOnly = notEditable;
postTxt.ReadOnly = notEditable;
}
private void UpdateDataBase()
{
if (MessageBox.Show("Customer information will be updated. This change cannot be undone. Are you sure you want to continue? ", "Confirm Edit", MessageBoxButtons.YesNo) == DialogResult.Yes)
{
string constring = #"Data Source=|DataDirectory|\LWADataBase.sdf";
string Query = "update customersTBL set ([First_Name] = '" + this.firstTxt.Text + "',surename= '" + this.surenameTxt.Text + "',[Address Line 1] = '" + this.address1Txt.Text + "',[Address Line 2] = '" + this.address2Txt.Text + "',County = '" + this.countyTxt.Text + "',[Post Code] = '" + this.postTxt.Text + "' , Email = '" + this.emailTxt.Text + "';,[Contact Number] = '" + this.contactTxt.Text + "');";
SqlCeConnection conDataBase = new SqlCeConnection(constring);
SqlCeCommand cmdDataBase = new SqlCeCommand(Query, conDataBase);
SqlCeDataReader myReader;
try
{
conDataBase.Open();
myReader = cmdDataBase.ExecuteReader();
MessageBox.Show("Customer information has been updated", "Update Sucessful");
while (myReader.Read())
{
}
MessageBox.Show("Please exit the Customers window and re-open to update the table");
this.Close();
//displays a system error message if a problem is found
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
There are some problems in your code.
One is trivial and could be easily fixed (remove the semicolon before the [Contact Number], but there are other hidden problems that potentially are more serious.
First: Remember to always close and dispose the disposable objects
(connection and command in this case). The using statement ensure
that the object enclosed by the using block will be correctly closed
and disposed also in case of exceptions
Second: Use a parameterized query. This avoids Sql Injections and
parsing problems. If one or more of your input data contains a single
quote, the string concatenation used to build the sql command text
will resul in an invalid command
Third: An update command acts on all the records present in the table
if you don't add a WHERE condition. Usually the WHERE condition is
added to identify the only record that need to be updated and it is
the value of a field with UNIQUE index or the PRIMARY KEY of your
table. Of course you could update more than one record with a less
restrictive WHERE clause but this doesn't seem to be the case
Fourth: Use the ExecuteNonQuery instead of ExecuteReader for commands
that update/insert the database (well it works equally but why use a
method that should be reserved for other uses?)
private void UpdateDataBase(int customerID)
{
string constring = #"Data Source=|DataDirectory|\LWADataBase.sdf";
string Query = #"update customersTBL set [First_Name] = #fname,
surename = #sur, [Address Line 1] = #addr1,
[Address Line 2] = #addr2, County = #county,
[Post Code] = #pcode, Email = #mail, [Contact Number] = #ctNo
WHERE customerID = #id";
using(SqlCeConnection conDataBase = new SqlCeConnection(constring))
using(SqlCeCommand cmdDataBase = new SqlCeCommand(Query, conDataBase))
{
try
{
conDataBase.Open();
cndDataBase.Parameters.AddWithValue("#fname", this.firstTxt.Text);
cndDataBase.Parameters.AddWithValue("#sur", this.surenameTxt.Text );
cndDataBase.Parameters.AddWithValue("#addr1", this.address1Txt.Text );
cndDataBase.Parameters.AddWithValue("#addr2", this.address2Txt.Text );
cndDataBase.Parameters.AddWithValue("#county", this.countyTxt.Text );
cndDataBase.Parameters.AddWithValue("#pcode", this.postTxt.Text );
cndDataBase.Parameters.AddWithValue("#mail", this.emailTxt.Text );
cndDataBase.Parameters.AddWithValue("#ctNo", this.contactTxt.Text );
cndDataBase.Parameters.AddWithValue("#id", customerID );
int rowsUpdated = cmdDataBase.ExecuteNonQuery();
if(rowsUpdate == 0)
MessageBox.Show("No customer found to update");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
As you can see, with a parameterized query is more difficult to write a bad sql text with hidden problems and the quoting job is passed to the database code that knows better how to format the parameter values.
The only problem that you need to solve is how to retrieve the value for the customerID or some other value that you could use in the WHERE clause to uniquely identify the record of your customer
In this point you call the UpdateDatabase method that now required a UserID variable containing the key to identify your user on the table
private void editBTN_Click(object sender, EventArgs e)
{
bool notEditable = true;
if (editBTN.Text == "Update")
{
// Here you need to identify uniquely your modified user
// Usually when you load the data to edit you have this info extracted from your
// database table and you have saved it somewhere
// (of course the user should not edit in any way this value
int UserID = ... ???? (from an hidden textbox? from a global variable, it is up to you
UpdateDataBase( UserID );
I think your confusing the Update structure with an Insert.
For your update it looks like this:
update customersTBL set ([First_Name] = 'data', surename= '',[Address Line 1] = '',[Address Line 2] = '',County = '',[Post Code] = '' , Email = '';,[Contact Number] = '');
You need a where clause.
Update/Set does not put the changes in ()
After email you have a ';'

What's wrong with my IF statement?

I'm creating an auditting table, and I have the easy Insert and Delete auditting methods done. I'm a bit stuck on the Update method - I need to be able to get the current values in the database, the new values in the query parameters, and compare the two so I can input the old values and changed values into a table in the database.
Here is my code:
protected void SqlDataSource1_Updating(object sender, SqlDataSourceCommandEventArgs e)
{
string[] fields = null;
string fieldsstring = null;
string fieldID = e.Command.Parameters[5].Value.ToString();
System.Security.Principal. WindowsPrincipal p = System.Threading.Thread.CurrentPrincipal as System.Security.Principal.WindowsPrincipal;
string[] namearray = p.Identity.Name.Split('\\');
string name = namearray[1];
string queryStringupdatecheck = "SELECT VAXCode, Reference, CostCentre, Department, ReportingCategory FROM NominalCode WHERE ID = #ID";
string queryString = "INSERT INTO Audit (source, action, itemID, item, userid, timestamp) VALUES (#source, #action, #itemID, #item, #userid, #timestamp)";
using (SqlConnection connection = new SqlConnection("con string = deleted for privacy"))
{
SqlCommand commandCheck = new SqlCommand(queryStringupdatecheck, connection);
commandCheck.Parameters.AddWithValue("#ID", fieldID);
connection.Open();
SqlDataReader reader = commandCheck.ExecuteReader();
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount - 1; i++)
{
if (reader[i].ToString() != e.Command.Parameters[i].Value.ToString())
{
fields[i] = e.Command.Parameters[i].Value.ToString() + "Old value: " + reader[i].ToString();
}
else
{
}
}
}
fieldsstring = String.Join(",", fields);
reader.Close();
SqlCommand command = new SqlCommand(queryString, connection);
command.Parameters.AddWithValue("#source", "Nominal");
command.Parameters.AddWithValue("#action", "Update");
command.Parameters.AddWithValue("#itemID", fieldID);
command.Parameters.AddWithValue("#item", fieldsstring);
command.Parameters.AddWithValue("#userid", name);
command.Parameters.AddWithValue("#timestamp", DateTime.Now);
try
{
command.ExecuteNonQuery();
}
catch (Exception x)
{
Response.Write(x);
}
finally
{
connection.Close();
}
}
}
The issue I'm having is that the fields[] array is ALWAYS null. Even though the VS debug window shows that the e.Command.Parameter.Value[i] and the reader[i] are different, the fields variable seems like it's never input into.
Thanks
You never set your fields[] to anything else than null, so it is null when you are trying to access it. You need to create the array before you can assign values to it. Try:
SqlDataReader reader = commandCheck.ExecuteReader();
fields = new string[reader.FieldCount]
I don't really understand what your doing here, but if your auditing, why don't you just insert every change into your audit table along with a timestamp?
Do fields = new string[reader.FieldCount] so that you have an array to assign to. You're trying to write to null[0].

Categories