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.
Related
I am new to SQL Server and C#. I am working on a login form that checks if the user is an admin or a basic user. In my SQL Server, I created a table that stores username, password, and role (admin or basic user). The saved data are the following:
For admin: username = admin, password = admin, role = admin
For basic user: username = user, password = user, role = user
If the user enters username and password "admin" it should be directed to the admin page else it would be user page.
This is my code:
string query = "SELECT * from tbl_login WHERE Username = #username and password=#password";
con.Open();
SqlCommand sqlcmd = new SqlCommand(query, con);
sqlcmd.Parameters.AddWithValue("#username", tbusername.Text);
sqlcmd.Parameters.AddWithValue("#password", tbpswlog.Text);
DataTable dtbl = new DataTable();
SqlDataAdapter sqlsda = new SqlDataAdapter(sqlcmd);
sqlsda.Fill(dtbl);
con.Close();
if (dtbl.Rows.Count == 1)
{
this.Hide();
if (tbusername.Equals("admin"))
{
MessageBox.Show("You are logged in as an Admin");
AdminHome fr1 = new AdminHome();
fr1.Show();
this.Hide();
}
else
{
MessageBox.Show("You are logged in as a User");
UserHome fr2 = new UserHome();
fr2.Show();
this.Hide();
}
}
else
{
MessageBox.Show("Incorrect username or password");
}
I know this code lacks and wrong.
EDIT:
I tried this code below
if (dtbl.Rows.Count == 1)
{
this.Hide();
if (dtbl.Rows[0]["role"].ToString().Equals("admin"))
{
MessageBox.Show("You are logged in as an Admin");
AdminHome fr1 = new AdminHome();
fr1.Show();
this.Hide();
}
else
{
MessageBox.Show("You are logged in as a User");
UserHome fr2 = new UserHome();
fr2.Show();
this.Hide();
}
}
else
{
MessageBox.Show("Incorrect username or password");
}
But even if I entered username admin and password admin, it would say "You entered as a user".
You only need to retrieve the value of the role. Don't retrieve unnecessary data. Check the name of the column in the database and correct if necessary.
Keep you database objects local so you can be sure they are closed and disposed. The using blocks that care of this even if there is an error.
You don't need a DataTable or a DataAdapter. Using .ExecuteScalar will return the first column of the first row of the result set which is exactly what you want to know. The role.
private void ValidateUser()
{
string query = "SELECT role from tbl_login WHERE Username = #username and password=#password";
string returnValue = "";
using (SqlConnection con = new SqlConnection("YourConnectionString"))
{
using (SqlCommand sqlcmd = new SqlCommand(query, con))
{
sqlcmd.Parameters.Add("#username", SqlDbType.VarChar).Value = tbusername.Text;
sqlcmd.Parameters.Add("#password", SqlDbType.VarChar).Value = tbpswlog.Text;
con.Open();
returnValue = (string)sqlcmd.ExecuteScalar();
}
}
//EDIT to avoid NRE
if (String.IsNullOrEmpty(returnValue))
{
MessageBox.Show("Incorrect username or password");
return;
}
returnValue = returnValue.Trim();
if (returnValue == "Admin")
{
MessageBox.Show("You are logged in as an Admin");
AdminHome fr1 = new AdminHome();
fr1.Show();
this.Hide();
}
else if (returnValue == "User")
{
MessageBox.Show("You are logged in as a User");
UserHome fr2 = new UserHome();
fr2.Show();
this.Hide();
}
}
Reading the comment, you need the following in the if statement instead of what you wrote.
if (tbusername.Text == "admin")
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..
I have code here where it will check if the entered username and password are correct, my problem is that in my database I have a Username: "ADMIN" and Password: "ADMIN" but whenever I try to input "admin" for both username and password it still allows me to go to the main window which means my bool was true.
Here is my code:
public bool IsAccountValid(string userLogin, string userPassword)
{
bool flag = false;
try
{
accessToDatabase.OpeningDatabase();
String query = "SELECT * FROM Users where Username=#Username AND Password=#Password";
SqlCommand sqlCmd = accessToDatabase.Command(query);
sqlCmd.CommandType = CommandType.Text;
sqlCmd.Parameters.AddWithValue("#Username", userLogin);
sqlCmd.Parameters.AddWithValue("#Password", userPassword);
if (sqlCmd.ExecuteScalar() != null)
flag = true;
else
flag = false;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
accessToDatabase.ClosingDataBase();
}
return flag; //returns false if query does not exists...
}
You can use ExecuteReader on SqlCommand to read values- which you can compare against user provided values. User Name shouldn't be case sensitive though.
My response assumes that username is unique in your user table - which i believe is pretty much valid assumption.
Since you only need username and pwd, you might want to modify your select query to :
SELECT UserName, Password
FROM Users
WHERE Username = #Username AND Password = #Password
Probably your revised snippet in that case would be something like :
public bool IsAccountValid(string userLogin, string userPassword)
{
bool flag = false;
try
{
accessToDatabase.OpeningDatabase();
String query = "SELECT * FROM Users where Username=#Username AND Password=#Password";
SqlCommand sqlCmd = accessToDatabase.Command(query);
sqlCmd.CommandType = CommandType.Text;
sqlCmd.Parameters.AddWithValue("#Username", userLogin);
sqlCmd.Parameters.AddWithValue("#Password", userPassword);
var reader = sqlCmd.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
if(userPassword == reader[0].ToString())
{
flag = true;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
accessToDatabase.ClosingDataBase();
}
return flag; //returns false if query does not exists...
}
Other alternative would be modify your sql query itself to ensure it takes care of case sensitive match in where clause like here
In that case, you need not to make any changes to your ADO.NET code.
Better way would be to change query for case sensitive search:
String query = "SELECT * FROM Users where
Username=#Username COLLATE SQL_Latin1_General_CP1_CS_AS
AND Password=#Password COLLATE SQL_Latin1_General_CP1_CS_AS";
Another method is do binary casting:
String query = "SELECT * FROM Users
where CAST(Username as varbinary(100))=CAST(#Username as varbinary(100))
AND CAST(Password as varbinary(100))=CAST(#Password as varbinary(100))";
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
I would like to ask how do I block a user after a specified failed attempts. After 30 minutes or whatever time, the user will be able to log in again.. Here's my sample code for log in.
public partial class Login : System.Web.UI.Page
{
SimplerAES AES = new SimplerAES();
protected void Page_Load(object sender, EventArgs e)
{
Session.Clear();
}
protected void btnLogIn_Click(object sender, EventArgs e)
{
if (txtUsername.Text == "" || txtPassword.Text == "")
{
lblMessage.Visible = true;
txtUsername.Text = "";
txtPassword.Text = "";
lblMessage.Text = "Invalid Username/Password";
}
else
{
SqlConnection con = new SqlConnection(SeiboLMS.Helper.GetConnectionString());
con.Open();
SqlCommand com = new SqlCommand();
com.Connection = con;
com.CommandType = CommandType.Text;
com.CommandText = "SELECT * FROM Users WHERE UserName=#UserName";
com.Parameters.Add("#UserName", SqlDbType.NVarChar);
com.Parameters[0].Value = txtUsername.Text;
SqlDataReader data = com.ExecuteReader();
if (data != null)
{
while (data.Read())
{
if (txtPassword.Text == AES.Decrypt(data["Password"].ToString()))
{
if (data["UserTypeID"].ToString() == "1")
{
Session["userid"] = data["UserID"].ToString();
Session["usertypeid"] = data["UserTypeID"].ToString();
Session["username"] = data["UserName"].ToString();
Session["password"] = data["Password"].ToString();
Helper.Logs(int.Parse(data["UserID"].ToString()), 1, "Log In Successful");
Response.Redirect("Admin/Default.aspx");
}
else
{
Session["userid"] = data["UserID"].ToString();
Session["usertypeid"] = data["UserTypeID"].ToString();
Session["username"] = data["UserName"].ToString();
Session["password"] = data["Password"].ToString();
Helper.Logs(int.Parse(data["UserID"].ToString()), 1, "Log In Successful");
Response.Redirect("Employees/Default.aspx");
}
}
else
{
lblMessage.Text = "Invalid Username/Password.";
txtUsername.Text = "";
txtPassword.Text = "";
}
}
}
else
{
lblMessage.Text = "Invalid Username/Password.";
txtUsername.Text = "";
txtPassword.Text = "";
}
data.Close();
con.Close();
con.Dispose();
}
}
}
Please share with me what ideas do you have there.
Thank you in advance..
This question has been answered many times.
Search for c# login with failed attempts
This particular question is very similar.
Login - Allow only 3 attempts
The top voted answer by Willem has the following text:
use a MembershipProvider and in your web.config, in system.web you can
configure number of attempts and timeouts. Set
maxInvalidPasswordAttempts="3" and passwordAttemptWindow="5" for your
requirements.
I suggest that if you find this helpful, you go vote up Willems answer.
You can use the MembershipProvider or depending on your database structure, create your own. Lots of ways to accomplish this, but one way is to include login_attempt, lockflag and timestamp columns. I'm guessing you have the login_attempt counter and are incrementing this (and resetting it to zero when successful) Then at login you check the values and if the user is locked check the timestamp to see when and if enough time has lapsed for the account to be unlocked.
Well, what you could do is the following:
1) Add in your users table, a field (DateTime) indicating the time that he/she was blocked by exceeds the maximum login attempts.
2) Add a key in the web.config (AppSettings, perhaps) that indicate how long users that had access blocked must wait until they can try again.
3) When trying to authenticate the user, check if the lock field (DateTime) is null, if it is the user performs the login normally. Otherwise compare the current time with the time it is in this field from the table, if greater than the appsettings value, prevents the user to login.
Hope I helped!