c# winforms query for hashed password in MySQL db - c#

Hello i have a problem i have website which has log in and register page. It works good and stores creds in my db correctly. (https://imgur.com/uuBvhQP) Now i need to be able to verify email and password from my c# login form but problem is passwords are hashed and i dont know what to do with it in my form.
I never used hashed passwords in my program so how it worked so far was my winform would take values from #email and #pass and compare it to db where password were not hashed.
private void db_connection()
{
try
{
conn = "my conn string";
connect = new MySqlConnection(conn);
connect.Open();
}
catch (MySqlException e)
{
throw;
}
}
private bool validate_login(string email, string pass)
{
db_connection();
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = "Select email, password from users where email=#user and password=#pass";
cmd.Parameters.AddWithValue("#email", user);
cmd.Parameters.AddWithValue("#pass", pass);
cmd.Connection = connect;
MySqlDataReader login = cmd.ExecuteReader();
if (login.Read())
{
connect.Close();
return true;
}
else
{
connect.Close();
return false;
}
}
private void button1_Click(object sender, EventArgs e)
{
string user = username.Text;
string pass = password.Text;
if (user == "" || pass == "")
{
MessageBox.Show("Empty Fields Detected ! Please fill up all the fields");
return;
}
bool r = validate_login(user, pass);
if (r)
MessageBox.Show("Correct Login Credentials");
else
MessageBox.Show("Incorrect Login Credentials");
}

As already suggested by others in the comments, you use the same algorithm to hash the user input you already used in the registration form. Then you compare the hashed input to the hashed data in your DB.
Please never store passwords in clear text or hashed with a weak algorithm. Many users always use the same password and if someone gets access to your DB, this obviously is a very bad thing..

Related

Query not executing as expected, returns -1 no matter what

I'm working on a user login system for a semester final. I am using C# in Visual Studio with ADO.NET. I have a query that I use on a database table named Credentials:
SELECT * FROM Credentials WHERE Username = #Username AND Password = #Password
I also have the string connection as
Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename=|DataDirectory|UserCredentials.mdf;Integrated Security=True;
I then run the query against the table with int count = selectCommand.ExecuteNonQuery();. selectCommand is the SqlCommand object with the above query in it. No matter what, count will equal -1 even if I enter an existing username and password. I want the count variable to be 1 when someone enters in a correct username and password combo.
class DatabaseConnection {
public static SqlConnection GetConnection() {
SqlConnection connection = new SqlConnection(
"Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename=|DataDirectory|UserCredentials.mdf;Integrated Security=True;"
);
return connection;
}
}
class LoginDB {
public static bool IsUser(string username, string password) {
SqlConnection connection = DatabaseConnection.GetConnection();
string selectStatement = #"SELECT * FROM Credentials
WHERE Username = #Username AND Password = #Password";
SqlCommand selectCommand = new SqlCommand(selectStatement, connection);
selectCommand.Parameters.AddWithValue("#Username", username);
selectCommand.Parameters.AddWithValue("#Password", password);
try {
connection.Open();
int count = selectCommand.ExecuteNonQuery();
MessageBox.Show(count.ToString());
if (count > 0) {
return true;
}
else {
return false;
}
}
catch (SqlException ex) {
throw ex;
}
finally {
connection.Close();
}
}
}
And then finally in the login form when the user presses login (The message box is just a placeholder for now):
private void btnLogin_Click(object sender, EventArgs e) {
if (LoginDB.IsUser(txtUsername.Text, txtPassword.Text)) {
MessageBox.Show("Logged in!");
}
}
Obviously this is just for a college project so don't say anything about how this is clearly an unsafe way to log users into a system.
ExecuteNonQuery is not what you want to execute. It is a query. You should use ExecuteReader which returns a SqlDataReader and then you can read how many rows are returned. Or ExecuteScalar will return only one column of one row.

How to make log-in form a case sensitive in C#?

Could somebody help me with my problem on my log-in form?
My username registered on the database is "admin" (all are in lowercase form). However, upon logging-in with username, "admiN" (considering N is capitalized), I still get logged-in successfully.
private void btnLogin_Click(object sender, EventArgs e)
{
Account account = new Account();
if (txtUserName.Text == "" || txtPassword.Text == "")
{
MessageBox.Show("Empty Fields Detected ! Please fill up all the fields");
return;
}
if (account.Authorize(txtUserName.Text, txtPassword.Text))
{
MessageBox.Show("Login Successfully!");
this.Hide();
main.showMeForm4(this);
}
else
{
txtPassword.Focus();
MessageBox.Show("Username or Password Is Incorrect");
txtUserName.Text = "";
txtPassword.Text = "";
}
}
//class Account
public bool Authorize(string userName, string userPassword)
{
Connection connection = new Connection();
string sql = "SELECT * FROM tbl_Account WHERE Username=#userName and Password=#userPassword";
MySqlConnection conn = new MySqlConnection(connection.ConnectionString);
MySqlCommand cmd = new MySqlCommand(sql, conn);
cmd.Parameters.AddWithValue("#userName", userName);
cmd.Parameters.AddWithValue("#userPassword", userPassword);
conn.Open();
MySqlDataReader login = cmd.ExecuteReader();
if (login.Read())
{
conn.Close();
return true;
}
else
{
conn.Close();
return false;
}
}
Your query will not take case into account. (default SQL Server behavior)
SELECT * FROM tbl_Account WHERE Username=#userName and Password=#userPassword
You can change your query to
SELECT * FROM tbl_Account
WHERE Username=#userName COLLATE SQL_Latin1_General_CP1_CS_AS
AND Password=#userPassword COLLATE SQL_Latin1_General_CP1_CS_AS
By changing the collation, it will take into account the case.
You can do what you want by doing the comparison in C# (instead of SQL) since string comparisons are case sensitive in C#:
MySqlDataReader login = cmd.ExecuteReader();
if (login.Read())
{
var userNameFromDb = login["Username"].ToString();
var passwordFromDb = login["Password"].ToString();
conn.Close();
return userNameFromDb == userName && passwordFromDb == userPassword
}
That being said, if this is for a something more than just your personal use / learning, I would recommend you reconsider how you are storing passwords. Right now, it looks like you're storing them in clear text which is a huge security risk. You should look into hashing and salting passwords and use a pre-made framework for authorization / authentication.
Also, I agree with other commenters that probably want to ignore case for the username.

Catching Button Tampering By a User C#

Login Error
As you can see, I want to catch the exception if the user is tampering the Login Button if there are no values in the fields or if it doesn't match info in the database.
For example:
The field has no values and I click Login button once, it says the error. After I clicked OK button, I click Login button again and now it says,
"ExecuteReader requires an open and available Connection. The connection's current state is closed."
I use 3 tier Architecture Windows Application.
BEL:
public SqlDataReader Login(BELLogin bellog)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = Con.getcon();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT username,password FROM tbl_login WHERE username = #Username AND password = #Password";
cmd.Parameters.AddWithValue("#Username", bellog.Acctname);
cmd.Parameters.AddWithValue("#Password", bellog.Password);
SqlDataReader dr = cmd.ExecuteReader();
return dr;
}
BAL:
public class BELLogin
{
public string Acctname { get; set; }
public string Password { get; set; }
}
DBConnection:
public SqlConnection getcon()
{
if (con.State == System.Data.ConnectionState.Closed)
con.Open();
else if (con.State == System.Data.ConnectionState.Open)
con.Close();
return con;
}
public DataTable ExeReader(SqlCommand cmd)
{
getcon();
cmd.Connection = getcon();
SqlDataReader dr = cmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dr);
return dt;
}
GUI:
private void btn_login_Click(object sender, EventArgs e)
{
BELog.Acctname = txb_accName.Text;
BELog.Password = txb_password.Text;
SqlDataReader dr;
dr = BALog.Login(BELog);
if (txb_accName.Text == "" || txb_password.Text == "")
{
MessageBox.Show("Some fields are empty. Please fill up all fields before clicking LOGIN button.", "Login Status", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
if (dr.HasRows == true)
{
dr.Read();
Inventory Inv = new Inventory();
Inv.Show();
this.Hide();
}
else
{
MessageBox.Show("You have entered your password or account name incorrectly. Please check your password and account name and try again.", "Login Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
dr.Close();
}
Logging in is ok but what if the user tampering the button?
Thank you for helping me :D
You need to change your code in gui like this:
//Put code to get the reader inside else clause and close the reader in the same else clause. Also ideally you should return if you encounter. I have added it and commented it.
//Off course you would need to put more effort to make this code better. You will get to that as you get more experience. For now this should make your app work.
private void btn_login_Click(object sender, EventArgs e)
{
BELog.Acctname = txb_accName.Text;
BELog.Password = txb_password.Text;
if (txb_accName.Text == "" || txb_password.Text == "")
{
MessageBox.Show("Some fields are empty. Please fill up all fields before clicking LOGIN button.", "Login Status", MessageBoxButtons.OK, MessageBoxIcon.Error);
//return;
}
else
{
SqlDataReader dr;
dr = BALog.Login(BELog);
if (dr.HasRows == true)
{
dr.Read();
Inventory Inv = new Inventory();
Inv.Show();
this.Hide();
}
else
{
MessageBox.Show("You have entered your password or account name incorrectly. Please check your password and account name and try again.", "Login Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
dr.Close();
}
}
Do not reuse connections like this, it is bad practice and unnecessary.
Wrap all type instances that implement IDisposable in using blocks so the resources are released. In your case SqlConnection, SqlCommand, SqlDataReader, DataTable.
From a security standpoint you should never store your user passwords (anywhere, not DB, not files, not registry, etc. just do not store them). You need to store the hash, not the password, and compare hashes
Adhere to loose the coupling / high cohesion principle. Essentially expose as little as possible (especially implementation details) from your methods / classes so they can be easily reused and changed. Currently you are passing around and sharing DB objects, this will make your code brittle and very difficult to track down where problems are. Here is your code with a little bit of refactoring, notice that if you have another issue with a connection during login it would now be very easy to figure out where that might be.
// place in new code file
public class UserManager{
public BELLogin FindLogin(string userName, string password){
if(string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
return null;
using(var connection = new SqlConnection("connectionStringPointerFromAppConfigHere"))
using(SqlCommand cmd = new SqlCommand("SELECT username,password FROM tbl_login WHERE username = #Username AND password = #Password", connection))
{
connection.Open();
cmd.Parameters.AddWithValue("#Username", bellog.Acctname).SqlDbType = SqlDbType.VarChar;
// BAD practice! Use a secure hash instead and store that not the password!
cmd.Parameters.AddWithValue("#Password", bellog.Password).SqlDbType = SqlDbType.VarChar;
using(SqlDataReader dr = cmd.ExecuteReader())
{
if(dr.Read())
return new BELLogin() {Acctname = dr.GetString(0), Password = dr.GetString(1)}; // passed in is same as in datareader
}
}
return null;
}
}
From your login form class
private void btn_login_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(txb_accName.Text) || string.IsNullOrEmpty(txb_password.Text))
{
MessageBox.Show("Some fields are empty. Please fill up all fields before clicking LOGIN button.", "Login Status", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
var manager = new UserManager();
var user = manager.FindLogin(txb_accName.Text, txb_password.Text);
if (user != null)
{
Inventory Inv = new Inventory();
Inv.Show();
this.Hide();
}
else
{
MessageBox.Show("You have entered your password or account name incorrectly. Please check your password and account name and try again.", "Login Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
Your btn_login_Click method appears to call BALog.Login(BELog) prior to checking if there are any valid values in the username and password textboxes. Simply move the validation to the top of btn_login_Click method, and return if the fields are empty:
if (txb_accName.Text == "" || txb_password.Text == "")
{
MessageBox.Show("Some fields are empty. Please fill up all fields before clicking LOGIN button.", "Login Status", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
The code in the else portion of that if statement can stay where it is, just not inside an else. The method will exit due to return statement if there are no valid values in the username and password textboxes.
As others have suggested, you should review your code to ensure you really want this structure; but if you do want to keep it that way, this simple fix will solve your problem.
Just get rid of your DBConnection object, it's not really doing anything and just making your structure complex:
public BELLogin Login(BELLogin bellog)
{
SqlConnection conn = new SqlConnection(connectionsString);
try
{
using (SqlCommand cmd = new SqlCommand())
{
conn.Open();
cmd.Connection = conn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT username,password FROM tbl_login WHERE username = #Username AND password = #Password";
cmd.Parameters.AddWithValue("#Username", bellog.Acctname);
cmd.Parameters.AddWithValue("#Password", bellog.Password);
//really this should be in a using as well.
//You be better off reading your data
//into a class and returnig the class not the reader.
using (SqlDataReader dr = cmd.ExecuteReader())
{
BELLogin obj = new BELLogin();
while(dr.Read())
{
//populate obj
}
return obj;
}
}
}
finally
{
conn.Close();
conn.Dispose();
}
}
also the way you use it can cause memory leaks as your not explicitly disposing and closing your connections. Always dispose Sql objects in C#. Be wary of exceptions too. Any exceptions in your code will not close the connection, etc. This will result in memory leaks and connections locking

Forms authentication and authorization that uses custom database tables? Asp.net web forms(C#).

Hi i am currently creating a web forms asp.net website, i am trying to add forms authentication with the following code for the login page below but i am now unable to login. I have all the appropriate code in the HTML and the web.config file and i am not receiving any errors. I may be going about the authentication for web forms wrong, any help would be appreciated. Thank You
private bool ValidateUser(string Username, string Password)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
SqlCommand cmd;
string lookupPassword = null;
// Check for invalid userName.
// userName must not be null and must be between 1 and 15 characters.
if ((null == Username) || (0 == Username.Length) || (Username.Length > 15))
{
System.Diagnostics.Trace.WriteLine("[ValidateUser] Input validation of userName failed.");
return false;
}
// Check for invalid passWord.
// passWord must not be null and must be between 1 and 25 characters.
if ((null == Password) || (0 == Password.Length) || (Password.Length > 25))
{
System.Diagnostics.Trace.WriteLine("[ValidateUser] Input validation of passWord failed.");
return false;
}
try
{
// Consult with your SQL Server administrator for an appropriate connection
// string to use to connect to your local SQL Server.
conn.Open();
// Create SqlCommand to select pwd field from users table given supplied userName.
cmd = new SqlCommand("Select password from customer where Username=#userName", conn);
cmd.Parameters.AddWithValue("#userName", TextBoxPassword.Text);
cmd.Parameters["#userName"].Value = Username;
// Execute command and fetch pwd field into lookupPassword string.
lookupPassword = (string)cmd.ExecuteScalar();
// Cleanup command and connection objects.
cmd.Dispose();
conn.Dispose();
}
catch (Exception ex)
{
// Add error handling here for debugging.
// This error message should not be sent back to the caller.
System.Diagnostics.Trace.WriteLine("[ValidateUser] Exception " + ex.Message);
}
// If no password found, return false.
if (null == lookupPassword)
{
// You could write failed login attempts here to event log for additional security.
return false;
}
// Compare lookupPassword and input passWord, using a case-sensitive comparison.
return (0 == string.Compare(lookupPassword, Password, false));
}
private void cmdLogin_ServerClick(object sender, System.EventArgs e)
{
if (ValidateUser(TextBoxUserN.Text, TextBoxPassword.Text))
FormsAuthentication.RedirectFromLoginPage(TextBoxUserN.Text,
chkPersistCookie.Checked);
else
Response.Redirect("login.aspx", true);
}
Click event's access modifier must be protected. Normally, VS throws Compilation Error when you browser the page.
protected void cmdLogin_ServerClick
{
...
}
You should try to break your idea in pieces and implement them, it will make the debug much more easy.
Short for Text box is txt, ex: txtUserName or txtPassword, full list here: https://msdn.microsoft.com/en-us/library/aa263493%28v=vs.60%29.aspx
Here is how this should be done in my opinion:
Validate username and password length, let user know if they are invalid (by displaying a message in the page, for example using a label called lblMessage, not "redirect to login.aspx", user don't know wth happened, the best is to use validation controls but you can use whatever...)
If (1) is valid, then check if username + password are matched in database, let user know if they are invalid ((by displaying a message in the page...).
If (2) is valid, redirect to appropriate page.
Here the code (syntax was not checked)
protected void cmdLogin_ServerClick(object sender, System.EventArgs e) {
try {
lblMessage.Text = "";
if (String.IsNullOrEmpty(TextBoxUserN.Text) || TextBoxUserN.Text.Length > 15) {
lblMessage.Text = "Username is invalid!";
return;
}
if (String.IsNullOrEmpty(TextBoxPassword.Text) || TextBoxPassword.Text.Length > 25) {
lblMessage.Text = "Password is invalid!";
return;
}
if (ValidateUser(TextBoxUserN.Text, TextBoxPassword.Text)) FormsAuthentication.RedirectFromLoginPage(TextBoxUserN.Text,
chkPersistCookie.Checked);
else lblMessage.Text = "Username and password combination is incorrect!";
} catch (Exception ex) {
lblMessage.Text = "An error has occurred, please try again later!";
System.Diagnostics.Trace.WriteLine("[ValidateUser] Exception " + ex.Message);
}
}
private bool ValidateUser(string username, string password) {
string CS = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
using(SqlConnection con = new SqlConnection(CS)) {
string query = "SELECT COUNT(*) FROM customer WHERE UserName = #username AND password = #password COLLATE SQL_Latin1_General_CP1_CS_AS"; //password is case SeNsiTive
SqlCommand cmd = new SqlCommand(query, con);
cmd.Parameters.AddWithValue("#username", username);
cmd.Parameters.AddWithValue("#password", password);
con.Open();
int i = Convert.ToInt32(cmd.ExecuteScalar());
//con.Close();
if (i == 1) return true //Correct username + password
else return false; //Wrong username + password
}
}

C# winform login using Ms Access 2013

I am creating a C# windows login form using MS Access 2013.
Login form using User ID(Autonumber) and Password(Short text).
My problem here is, it always crash(or Syntax error I guess) every time I click the LOGIN button and I can't trace the problem since I'm still inexperience in programming.
Table User
Fields: user_Id(Auto Number), password(short text), name(short text), type(number)
private void btn_Login_Click(object sender, EventArgs e)
{
try
{
if (string.IsNullOrEmpty(txt_UserId.Text))
{
lbl_warningUser.Visible = true;
lbl_warningUser.Text = "User ID is Empty";
}
if (string.IsNullOrEmpty(txt_Password.Text))
{
lbl_warningPass.Visible = true;
lbl_warningPass.Text = "Password is Empty";
}
if (txt_UserId.Text !="" & txt_Password.Text != "")
{
string constring = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Database/Health.accdb;";
string query = ("SELECT COUNT(*) FROM User WHERE user_Id = #ID AND password = #Pass");
using (OleDbConnection con = new OleDbConnection(constring))
using (OleDbCommand cmd = new OleDbCommand(query, con))
{
con.Open();
cmd.Parameters.AddWithValue("#ID", txt_UserId.Text);
cmd.Parameters.AddWithValue("#Pass", txt_Password.Text);
int result = (int)cmd.ExecuteScalar();
if (result > 0)
{
MessageBox.Show("Successfully Login");
con.Close();
this.Hide();
MainUI m = new MainUI();
m.Show();
}
else
{
MessageBox.Show("Incorrect User ID or Password");
}
con.Close();
}
}
}
catch (Exception ex)
{
MessageBox.Show(" "+ex);
}
}
User is a reserved word in MS Access I think, so you need to wrap it in square bracket delimiters. Also, you likely need to specify an alias for the COUNT function result:
"SELECT COUNT(*) AS qtyUsers FROM [User] WHERE user_Id = #ID AND password = #Pass"
txt_UserId.Text has a default return value of string, did you convert the value to an integer then try actually autonumber is integer.
First convert your value and try
Convert.ToInt32(txt_UserId.Text)

Categories

Resources