I'm trying to store hashed passwords in db. Here is my code:
string passwords = textBox2.Text;
string salt = BCrypt.Net.BCrypt.GenerateSalt(12);
string hashPwd = BCrypt.Net.BCrypt.HashPassword(passwords, salt);
try
{
SQLiteCommand command = new SQLiteCommand();
connection.Open();
command.Connection = connection;
command.CommandText = ((#"INSERT INTO acc (UserName, Pass) VALUES ('" + textBox1.Text + "','" + hashPwd+ "');"));
command.ExecuteNonQuery();
connection.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error:" + ex.ToString());
return;
}
Login/verification code:
try
{
SQLiteDataAdapter sqlad = new SQLiteDataAdapter("SELECT COUNT(*) From acc WHERE Username = '" + textBox1.Text + "' AND Pass = '" + textBox2.Text + "' ", connection);
DataTable dt = new DataTable();
sqlad.Fill(dt);`
string userid = dt.Rows[0]["UserName"].ToString();
string password = dt.Rows[0]["Pass"].ToString();
bool flag = BCrypt.Net.BCrypt.Verify(textBox2.Text, password);
if (userid == textBox1.Text && flag == true)
{
Form2 frm = new Form2();
frm.Show();
}
else
{
MessageBox.Show("Invalid UserId or password");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
return;
}
I can't verify Password, I'm getting error, could you help me please? One more question, should I save salt too in database?
There are a couple of problems with your code:
1. SQL Injection
Both your insert and verification code blocks are vulnerable to SQL injection, since they allow text you take directly from user input into the SQL string executed, a vulnerability they can use to either subvert the login check, or destroy your database. Don't do it!
2. Your selection of the hashed password back from the database does not select the hashed password.. or anything of interest.
Consider what you have here:
SQLiteDataAdapter sqlad = new SQLiteDataAdapter(#"
SELECT
COUNT(*)
From
acc
WHERE
Username = '" + textBox1.Text + "'
AND
Pass = '" + textBox2.Text + "' ", connection);
So, let's say I gave my username as "Steve" and password as "hello", which got hashed to "ab123cdef", and inserted to your acc table as:
UserName Pass
Steve ab123cdef
And when I come to verify this with the original correct user and password, your select statement says "give me the number of rows with username 'Steve' and pass 'hello'", which will duly return zero.
Your code should throw an exception here:
string userid = dt.Rows[0]["UserName"].ToString();
Since the result set doesn't contain the username as an output.
Here is a basic little example using the libraries you've chosen to show how you could insert and verify a password successfully.
Regarding what to do with the salt, the function HashPassword has prepended the salt to the password hash, so if you store the output of this, you are storing the salt. The verify function you use in verification will handle and check this for you.
static void CreateUser(string username, string password)
{
if (UserExists(username))
throw new InvalidOperationException("User already exists");
string salt = BCrypt.Net.BCrypt.GenerateSalt(12);
// if you look at the hashed password, notice that it's prepended with the salt generated above
string hashedPassword = BCrypt.Net.BCrypt.HashPassword(password, salt);
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
SQLiteCommand insertCommand = new SQLiteCommand(connection);
insertCommand.CommandText = #"INSERT INTO acc (UserName, Pass) VALUES (#username, #hashedPass);";
// use parameterised queries to mitigate sql injection
insertCommand.Parameters.Add(new SQLiteParameter("#username", username));
insertCommand.Parameters.Add(new SQLiteParameter("#hashedPass", hashedPassword));
insertCommand.ExecuteNonQuery();
}
}
To verify a given username/password, all we need back from the database is the output of the hash function to verify against what we've been given.
static bool Verify(string username, string password)
{
using (SQLiteConnection connection = new SQLiteConnection(connectionString))
{
connection.Open();
SQLiteCommand checkUserCommand = new SQLiteCommand(connection)
{
CommandText = #"SELECT Pass FROM acc WHERE UserName = #username;"
};
checkUserCommand.Parameters.Add(new SQLiteParameter("#username", username));
var hashedPassword = (string)checkUserCommand.ExecuteScalar();
return BCrypt.Net.BCrypt.Verify(password, hashedPassword);
}
}
Usage would be something like..
if (!UserExists(username))
{
CreateUser(username, password);
Console.WriteLine("User {0} created", username);
}
else
{
bool loginOk = Verify(username, password);
Console.WriteLine("Login ok?: {0}", loginOk);
}
Related
i have a little problem. My Script lets users login with a random password. How can i fix it? Here are all informations: Passworts are stored in MySQL DB V8, and they crypted correctly with BCrypt.
Bcrypt Code:
private static string GetRandomSalt()
{
return BCrypt.Net.BCrypt.GenerateSalt(10);
}
public static string HashPassword(string password)
{
return BCrypt.Net.BCrypt.HashPassword(password, GetRandomSalt());
}
public static bool ValidatePassword(string username, string password)
{
return BCrypt.Net.BCrypt.Verify(username, password);
}
This is my code where i got the problem:
[RemoteEvent("loginUser")]
public void loginUserEvent(Client player, String username, String password)
{
if (player.HasData("waitLogando"))
{
player.SendNotification("Wait...");
return;
}
player.SetData("waitLogando", true);
using (MySqlConnection Mainpipeline = new MySqlConnection(Main.myConnectionString))
{
Mainpipeline.Open();
MySqlCommand query = Mainpipeline.CreateCommand();
query.CommandType = CommandType.Text;
query.CommandText = "SELECT * FROM `users` WHERE ( `Username` = '" + username + "' OR `email` = '" + username + "')";
query.ExecuteNonQuery();
DataTable dt = new DataTable();
using (MySqlDataAdapter da = new MySqlDataAdapter(query))
{
da.Fill(dt);
int i = 0;
i = Convert.ToInt32(dt.Rows.Count.ToString());
if (i == 0)
{
string query2 = "SELECT * FROM users (username, password) VALUES (#username, #password)";
MySqlCommand LoginAccount = new MySqlCommand(query2, Mainpipeline);
LoginAccount.Parameters.AddWithValue("#username", "" + username + "");
LoginAccount.Parameters.AddWithValue("#password", "" + AccountManage.ValidatePassword(username, password) + "");
LoginAccount.ExecuteNonQuery();
player.SendNotification("Wrong password");
player.ResetData("waitLogando");
}
else
{
NAPI.ClientEvent.TriggerClientEvent(player, "clearLoginWindow");
AccountManage.LoadAccount(player, username);
player.ResetData("waitLogando");
}
}
}
}
I really hope you can help me, thanks for your time! If you need more informations, im here.
You are not checking the password in the first query, you only check user name or email.
Ah, query2 is also incorrect (did you mean INSERT instead of SELECT?).
I make a user and save this in an SQLite database. The user gets a password and a salt.
public bool SaveNewUser(string databaseFileName)
{
bool faulted = true;
string SQL = string.Empty;
using (SQLiteConnection m_dbConnection = new SQLiteConnection("Data Source=" + databaseFileName))
{
var passwordSalt = EncryptDecryptUserData.GenerateSalt();
var passwordHash = EncryptDecryptUserData.ComputeHash(Password, passwordSalt);
var usereroleHash = EncryptDecryptUserData.ComputeHash(UserName + Role_Name, passwordSalt); //to avoid copy role to an other user the combi username+rolenam ara saved
if (Id == -1) //-New User =
{
SQL = "insert into QB_USER_LIST (GUID, USERNAME, PASSWORD, SALT, ROLE_NAME, GROUP_NAME, DATUM_AANGEMAAKT, AANGEMAAKT_DOOR, USER_ROLE ) ";
SQL += "values (#GUID, #USERNAME, #PASSWORD, #SALT ,#ROLE_NAME, #GROUP_NAME, #DATUM_AANGEMAAKT, #AANGEMAAKT_DOOR, #USER_ROLE )";
}
else
{
//change existing user
SQL = "UPDATE QB_USER_LIST set USERNAME = #USERNAME,";
}
m_dbConnection.Open();
SQLiteCommand command = new SQLiteCommand(SQL, m_dbConnection);
command.Prepare();
command.Parameters.Add(new SQLiteParameter("#USERNAME", UserName));
if (Id == -1)
{
command.Parameters.Add(new SQLiteParameter("#PASSWORD", Convert.ToBase64String(passwordHash)));
command.Parameters.Add(new SQLiteParameter("#SALT", Convert.ToBase64String(passwordSalt)));
command.Parameters.Add(new SQLiteParameter("#ROLE_NAME", Role_Name));
command.Parameters.Add(new SQLiteParameter("#GROUP_NAME", Group_Name));
command.Parameters.Add(new SQLiteParameter("#DATUM_AANGEMAAKT", DateTime.Now));
command.Parameters.Add(new SQLiteParameter("#AANGEMAAKT_DOOR", Environment.UserName));
command.Parameters.Add(new SQLiteParameter("#GUID", Guid));
command.Parameters.Add(new SQLiteParameter("#USER_ROLE", Convert.ToBase64String(usereroleHash)));
}
try
{
command.ExecuteNonQuery();
Logging.WriteToLog("INFORMATIE", "De gegevens van een nieuwe gebruiker zijn opgeslagen. (Nieuwe gebruiker = "+ UserName + ")."); //AANGEPAST
faulted = false;
}
Then i want to change data from the user and use the same salt
string Salt = GetUserSalt(databaseFileName); //This is "select salt from QB_USER_LIST"
var passwordSalt = Convert.FromBase64String(Salt); //Get the excisting salt
var passwordHash = EncryptDecryptUserData.ComputeHash(Password, passwordSalt); //create the new paspordhash with the same salt --> goes wrong
I seems like i can't use the same salt again. I want to re use it because I use the salt to hash the field UserRole. Later when I want to change just the UserRole of a user I need to compare it with the salt.
Found it. The password in my verifypassword function was not the password but the hashed password. So i was comparing 2 different things.
I want to login to the program using c#, with my username and password that's stored to the SQL Database in phpmyadmin.
This is what I have so far.
private void button1_Click(object sender, EventArgs e)
{
MySqlConnection connection;
string server = "localhost";
string database = "login";
string uid = "root";
string password = "";
string connectionString;
connectionString = "SERVER=" + server + ";" + "DATABASE=" +
database + ";" + "UID=" + uid + ";" + "PASSWORD=" + password + ";";
connection = new MySqlConnection(connectionString);
try
{
connection.Open();
if (connection.State == ConnectionState.Open)
{
connection.Close();
Form1 frm = new Form1(this);
frm.Show();
Hide();
}
else
{
MessageBox.Show("Database Connection Failed", "Epic Fail", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);
}
}
catch (Exception ex)
{
MessageBox.Show("An Error Occured, Try again later.", "Epic Fail", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk);
}
}
It connects to the database, however I don't want it to show the form1 Until both a valid Username and Password have been entered.
I'm guessing I need to use SELECT * FROM but I'm not exactly sure how to go about it.
You can use this way to see if username and password match
MySqlCommand cmd = dbConn.CreateCommand();
cmd.CommandText = "SELECT count(*) from tbUser WHERE UserName = #username and password=#password";
command.Parameters.Add("#username", txtUserName.Text);
command.Parameters.Add("#password", txtPassword.Text);
var count = cmd.ExecuteScalar();
if(count>0)
//Logged In
Just to say, if you use a query like
cmd.CommandText = "SELECT count(*) from tbUser WHERE UserName = '"+txtusernam +"'";
You will be open to SQL Injection
Warning
As Steve mentioned in comments Passwords in clear text are a vulnerability of the same magnitude of string concatenation
you make try this one
using(var con = new MysqlConnection{ ConnectionString = "your connection string " })
{
using(var command = new MysqlCommand{ Connection = con })
{
con.Open();
command.CommandText = #"SELECT level FROM userTable WHERE username=#username, password=#password";
command.AddWithValue("#username", txtusername.Text);
command.AddWithValue("#password", txtpassword.Text);
var strLevel = myCommand.ExecuteScalar();
if(strLevel == DBNULL.Value || strLevel == Null)
{
MessageBox.Show("Invalid username or password");
return;
}
else
{
MessageBox.Show("Successfully login");
hide(); // hide this form and show another form
}
}
}
use below Query
Select * from UsersTable Where Username='"+username+"' AND password='"+password+"'
Then you can make a if condition that if your query contain a result (rows) then users authenticated (exists in Table)
Note:Select query may fetch multiple users having same userName and
password, its upto you to keep usersname unique in table
I have hased my password right there on in the registration.aspx. having this code in my business layer:
public static string CreateSHAHash(string Phrase)
{
SHA512Managed HashTool = new SHA512Managed();
Byte[] PhraseAsByte = System.Text.Encoding.UTF8.GetBytes(string.Concat(Phrase));
Byte[] EncryptedBytes = HashTool.ComputeHash(PhraseAsByte);
HashTool.Clear();
return Convert.ToBase64String(EncryptedBytes);
}
and this code in the register page:
scm.Parameters.AddWithValue("#Password", BusinessLayer.ShoppingCart.CreateSHAHash(txtPW.Text));
Having the codes above, the password are being hashed in the DB and it is working fine when I log in with this code:
protected void btn_Login_Click(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString);
conn.Open();
string checkuser = "select count(*) from UserData where Username = '" + txtUser.Text + "'";
SqlCommand scm = new SqlCommand(checkuser, conn);
int temp = Convert.ToInt32(scm.ExecuteScalar().ToString());
conn.Close();
if (temp == 1)
{
conn.Open();
string checkPassword = "select Password from UserData where Username ='" + txtUser.Text + "'";
SqlCommand passCom = new SqlCommand(checkPassword, conn);
string password = passCom.ExecuteScalar().ToString();
if (password == BusinessLayer.ShoppingCart.CreateSHAHash(txtPassword.Text))
{
Session["New"] = txtUser.Text;
Response.Write("<script>alert('Logged In')</script>");
Response.Redirect("OrderNow.aspx");
}
else
{
lblcrederror.Text = ("Credentials dont match");
}
}
else
{
lblcrederror.Text = ("Credentials dont match");
}
However when I change it having this code in my changepassword.aspx, its not letting me in with my new password.
protected void btn_update_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(conn);
con.Open();
str = "select * from UserData ";
com = new SqlCommand(str, con);
SqlDataReader reader = com.ExecuteReader();
while (reader.Read())
{
if (BusinessLayer.ShoppingCart.CreateSHAHash(txt_cpassword.Text) == reader["Password"].ToString())
{
up = 1;
}
}
reader.Close();
con.Close();
if (up == 1)
{
con.Open();
str = "update UserData set Password=#Password where UserName='" + Session["New"].ToString() + "'";
com = new SqlCommand(str, con);
com.Parameters.Add(new SqlParameter("#Password", SqlDbType.VarChar, 50));
com.Parameters["#Password"].Value = BusinessLayer.ShoppingCart.CreateSHAHash(txt_npassword.Text);
com.ExecuteNonQuery();
con.Close();
lbl_msg.Text = "Password changed Successfully";
}
else
{
lbl_msg.Text = "Please enter correct Current password";
}
}
What am I missing here?
Check if the 50 truncates the hash.
com.Parameters.Add(new SqlParameter("#Password", SqlDbType.VarChar, 50));
On a sidenote i see that your solution is very open to SQL injection.
"select Password from UserData where Username ='" + txtUser.Text + "'";
A user can write sql statements in the textbox, and hijack your database, create his own tables or drop the whole database. You should always parameterize the queries. I see that you did that to the Update statement, but you should consider doing it for all of your variables.
This quickly creates a lot of code, so i would also consider making an SQL wrapper, that wraps in all of the things you repeat. When you are done refactoring it could look something like this:
var sql = new SqlWrapper("select Password from UserData where Username = #username", txtUser.Text);
var dataSet = sql.Execute();
Then you can hide all of your connectionstring, commands++ behind this wrapper and only tell the wrapper what you actually care about.
You should also consider using a salt for your password. If you and I have the same password, the hash will be the same. A salt will fix this problem.
A good article about password security -> https://crackstation.net/hashing-security.htm
I have created the following WebMethod in the back end of my application where the users login through the front end.
[WebMethod]
public String Login(String userName, String password)
{
OleDbConnection connect = new OleDbConnection(connection);
connect.Open();
OleDbCommand command = new OleDbCommand("Select * from login where userName='" + userName + "' and password ='" + password + "'", connect);
command.CommandType = CommandType.Text;
OleDbDataAdapter adapter = new OleDbDataAdapter();
adapter.SelectCommand = command;
DataSet NSNSet = new DataSet();
adapter.Fill(NSNSet);
string username = NSNSet.Tables[0].Rows[0]["firstName"].ToString() + NSNSet.Tables[0].Rows[0]["lastName"].ToString();
int userID = System.Convert.ToInt16(NSNSet.Tables[0].Rows[0]["UID"].ToString());
return username + "," + userID;
}
Currently, I have error handling in place which states -
catch(Exception ex)
{
string error = System.Convert.ToString(ex);
if (error.Contains("There is no row at position 0"))
{
status.Text = "Incorrect Username/Password combination";
}
}
This works fine, however how could I aulter my code so that it brings back a more specific error, i.e. states if the userName or password specifically are incorrect?
Don't give out to much details, just give a simple login error message, but don't say that username is incorrect or password is incorrect, cause a hacker can use that information
a simple text saying login unsuccessful should be ok
You should do like this:
public String Login(String userName, String password)
{
OleDbConnection connect = new OleDbConnection(connection);
connect.Open();
OleDbCommand command = new OleDbCommand("Select UID, firstName, lastName from login where userName=? and password =?", connect);
command.CommandType = CommandType.Text;
//to avoid sql injection
command.Parameters.Add(userName);
command.Parameters.Add(password);
OleDbDataAdapter adapter = new OleDbDataAdapter();
adapter.SelectCommand = command;
DataSet NSNSet = new DataSet();
adapter.Fill(NSNSet);
if (NSNSet.Tables[0].Rows.Count == 0)
return "Access denied";
string username = NSNSet.Tables[0].Rows[0]["firstName"].ToString() + NSNSet.Tables[0].Rows[0]["lastName"].ToString();
int userID = int.Parse(NSNSet.Tables[0].Rows[0]["UID"].ToString());
return username + "," + userID;
}
Or a better way, using DataReader for performance:
public String Login(String userName, String password)
{
OleDbConnection connect = new OleDbConnection(connection);
connect.Open();
OleDbCommand command = new OleDbCommand("Select UID, firstName, lastName from login where userName=? and password =?", connect);
command.CommandType = CommandType.Text;
//to avoid sql injection
command.Parameters.Add(userName);
command.Parameters.Add(password);
OleDbDataReader reader=command.ExecuteReader();
if (reader.Read())
{
//that means there's at least one row
string username = reader["firstName"] + " " + reader["lastName"];
int userID = int.Parse(reader["UID"].ToString());
return username + "," + userID;
}
else
{
//no combination username-password found
return "Access denied";
}
}
First, this code is open to SQL injection. Second, if you want to know specifically which element is incorrect, you have to break down your query into two components (ie. query username and password separately)
You can change you select query a little bit to this:
"select * from login where userName='"+userName+"'";
if there is no row in DataSet then write
Invalid UserName
and if user exist then check if password match or not if not match then write
Invalid Password