Reputation: 83
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?
Upvotes: 0
Views: 5156
Reputation: 5812
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);
}
Upvotes: 4