ADODB.Command, finding rows that satisfy a condition - c#

The work is done in C#, with an Access database to connect to.
Currently, I want to retrieve the number of accounts from the table ACCOUNT_T that satisfy the user's inputted credentials (username, email, password).
The table has 3 attributes: acc_username VARCHAR(30), acc_email VARCHAR(50), and acc_password VARCHAR(30)
The table has only one entree: 'Tester', 'test#mail.com', 'TestPass'
I want to check number of rows/entrees in the database that match the user's inputted credentials (every account is unique, so assume no duplicates), and used the code shown below.
//Checks whether the user has entered the correct credentials
//If correct info is entered, redirect user to the Main Menu page
private void Login_Login_Button_Click(object sender, EventArgs e)
{
//Open connection
ADODB.Connection connection = new ADODB.Connection();
connection.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=main_db;Jet OLEDB:Engine Type=5;";
connection.Open();
//Create command and object
ADODB.Command command = new ADODB.Command();
object rowsAffected;
//Setting up command and parameters
command.ActiveConnection = connection;
command.CommandText = "SELECT COUNT(*) FROM ACCOUNT_T WHERE acc_username = \'#USERNAME\' AND acc_email = \'#EMAIL\' AND acc_password = \'#PASSWORD\'";
command.Parameters.Append(command.CreateParameter("#USERNAME", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 200, Login_Username_TextBox.Text));
command.Parameters.Append(command.CreateParameter("#EMAIL", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 200, Login_Email_TextBox.Text));
command.Parameters.Append(command.CreateParameter("#PASSWORD", DataTypeEnum.adVarChar, ParameterDirectionEnum.adParamInput, 200, Login_Password_TextBox.Text));
//Execute command and store into RecordSet
ADODB.Recordset recordSet = command.Execute(out rowsAffected);
//Output A
MessageBox.Show(recordSet.RecordCount.ToString());
//Output B
MessageBox.Show(((int)rowsAffected).ToString());
connection.Close();
if ((int)rowsAffected == 1)
{
MainMenu_User_Label.Text = "Logged In As: " + Login_Username_TextBox.Text;
SetupPanel(MainMenu_Panel);
}
else
{
MessageBox.Show("Wrong Credentials.", "Login Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
However, as marked above, Output A gives the value -1 for "recordSet.RecordCount.ToString()" and Output B gives 0 for "((int)rowsAffected).ToString()". The output is the same regardless of what the user input is, right or wrong. (Meaning that the same output is given whether the user's inputted data is already in the database or not)
Is there something wrong with the code?

private string ConnectionString {get { return "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=main_db;Jet OLEDB:Engine Type=5;"; } };
private void Login_Login_Button_Click(object sender, EventArgs e)
{
string sql = "
SELECT COUNT(*)
FROM ACCOUNT_T
WHERE acc_username = ? AND acc_email = ? AND acc_password = ?";
int rowsAffected = 0;
using (var connection = new OleDbConnection(ConnectionString))
using (var command = new OleDbCommand(sql, connection))
{
// Use OleDbType enum values to match database column types and lengths.
// I have to guess, but you can get exact values from your database.
// Also, OleDb uses positional parameters, rather than names.
// You have to add the parameters in the order they appear in the query string.
command.Parameters.Add("acc_username", OleDbType.VarChar, 200).Value = Login_Username_TextBox.Text;
command.Parameters.Add("acc_email", OleDbType.VarChar, 200).Value = Login_Email_TextBox.Text;
command.Parameters.Add("acc_password", OleDbType.VarChar, 200).Value = Login_Password_TextBox.Text;
cn.Open();
rowsAffected = (int)command.ExecuteScalar();
} //leaving the using block will guarantee the connection is closed, even if an exception is thrown
MessageBox.Show(rowsAffected.ToString());
if (rowsAffected == 1)
{
MainMenu_User_Label.Text = "Logged In As: " + Login_Username_TextBox.Text;
SetupPanel(MainMenu_Panel);
}
else
{
MessageBox.Show("Wrong Credentials.", "Login Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
While I'm here, I also need to mention this is exceptionally poor password handling. It is NEVER okay to store a password like this, even for a simple personal or testing app. This is one of those things that's too important to do wrong, even in learning code. The problem is people tend to re-use passwords, so a breach for your simple testing app might also provide an attacker with credentials which grant access to something far more important. Just don't do it.
Instead, you must create a unique salt (or nonce) value for each user. When a user sets the password, you prepend the salt to the new password. Then you create a cryptographic hash of the combined value using an algorithm like BCrypt and prepend the salt to final value again. Now you only store this altered information. Never store the actual password. When someone tries to login, you retrieve the stored information, extract the salt, and use the same procedure on the attempted password. Now you can compare the hash values rather than the raw passwords.

Related

Login Button to read sql database and confirm values entered into textboxes with values in said sql database throws error

I am a noob attempting to create a login page where the user enters their username and password that is already in the sqldatabase connected to the textboxes/button/form. The below code is my best attempt at doing so, but upon debugging it throws catch despite the textbox values entered being registered in the sql database. If any additional information is needed please ask.
private bool compareStoD(string teststring1, string teststring2)
{
return String.Compare(teststring1, teststring2, true, System.Globalization.CultureInfo.InvariantCulture) == 0 ? true : false;
}
private void button1_Click_1(object sender, EventArgs e)
{
try
{
SqlConnection connection = new SqlConnection(#"Data Source=DESKTOP-P3JSE1C;Initial Catalog=logins;Integrated Security=True");
connection.Open();
SqlCommand checker = new SqlCommand("SELECT COUNT (*) from users WHERE username='" + textBox1.Text + "'AND pssword='" + textBox3.Text + "'", connection);
SqlDataReader reader = checker.ExecuteReader();
string usernameText = textBox1.Text;
string psswordText = textBox3.Text;
while (reader.Read())
{
if (this.compareStoD(reader["username"].ToString(), textBox1.Text) && // replace textbox1.Text with text string usernameText
this.compareStoD(reader["pssword"].ToString(), textBox3.Text)) //replace textbox3.Text with text string psswordText
{
main wen = new main();
wen.Show();
}
}
reader.Close();
connection.Close();
}
catch
{
MessageBox.Show("Incorrect password or username.");
}
}
It is most likely throwing an exception because your query is asking for the count but then you are reading columns username and password which do not exist in the reader. This is your query:
SELECT COUNT (*)
Change that to this:
SELECT username, password ...
Also, unless you want every savvy user to access your application, use SqlParameter to avoid SQL Injection
Another Suggestion
I am not sure what main is, my assumption it is some window, but I would not show it where you are showing right now. Try to close the reader as soon as possible and then show the window if the user is authenticated like this.
bool userIsAuthenticated = false;
if (reader.Read())
{
// if a row was returned, it must be the row for the user you queried
userIsAuthenticated = true;
}
reader.Close();
connection.Close();
// Now that the reader is closed, you can show the window so the reader does not stay
// open during the duration of the main window
if (userIsAuthenticated)
{
main wen = new main();
wen.Show();
}
Select count returns the count not the row, if you want the row itself change to select username, password instead of select count(*) . See this link
There is over work being done by your code. You are querying the database by comparing the username and password values from UI to the values in the table. And once and if values are retrieved from the database you are again comparing value from UI to the values coming from the database. This is unnecessary.
The query will return the values only if values match in the database so you don't need to compare them again. So method compareStoD is not required at all.
The button1_Click can be changed as following to make it simpler.
private void button1_Click_1(object sender, EventArgs e)
{
try
{
SqlConnection connection = new SqlConnection(#"Data Source=DESKTOP-P3JSE1C;Initial Catalog=logins;Integrated Security=True");
connection.Open();
SqlCommand checker = new SqlCommand("SELECT COUNT (*) from users WHERE username=#userName AND pssword = #password", connection);
checker.Parameters.Add(new SqlParameter("#userName", textBox1.Text));
checker.Parameters.Add(new SqlParameter("#password", textBox3.Text));
var count = Convert.ToInt32(checker.ExecuteScalar());
connection.Close();
if(count > 0)
{
main wen = new main();
wen.Show();
}
else
{
MessageBox.Show("Incorrect password or username.");
}
}
catch
{
MessageBox.Show("Incorrect password or username.");
}
}
Also one good practice while supplying values from Textbox, you should use Textbox.Text.Trim() which helps in eliminating the spaces at the beginning and end. These spaces can create a problem in later stage.

i don't input any accounts but in still logs in

private void btnLogin_Click(object sender, EventArgs e)
{
{
Connections.con.Open();
string login = "SELECT ID, Username, [Password] FROM Employee";
OleDbCommand command = new OleDbCommand(login, Connections.con);
command.Connection = Connections.con;
command.Parameters.AddWithValue("#?", txtLUser.Text.ToString());
command.Parameters.AddWithValue("#?", txtLPass.Text.ToString());
OleDbDataReader reader = command.ExecuteReader();
int count = 0;
while (reader.Read())
{
count = count + 1;
break;
}
if (count == 1)
{
MessageBox.Show("Login Successful.");
this.Close();
}
else
{
MessageBox.Show("Please enter a valid Username or Password");
}
Connections.con.Dispose();
Connections.con.Close();
MessageBox.Show("Thank you for using this Simple Login/Registration Form.");
}
It always Logs in whenever i click Login Button and i haven't even typed anything in the user/pass textbox and there is no blank registered in my access database
any advice?
You're not actually checking the username and password. Look at the database query:
"SELECT ID, Username, [Password] FROM Employee"
This will select every record from the Employee table. Then you check those records:
while (reader.Read())
{
count = count + 1;
break;
}
if (count == 1)
{
MessageBox.Show("Login Successful.");
this.Close();
}
According to this logic, as long as any record exists in the Employee table, the login is successful.
You probably want to check only for records which match the supplied credentials. Something like this:
"SELECT [ID], [Username], [Password] FROM [Employee] WHERE [Username] = #? AND [Password] = #?"
(I'm guessing on the parameter syntax based on how you add the parameters, since I'm not familiar with MS Access syntax. But hopefully you get the idea.)
Also, and this is important, you appear to be storing user passwords in plain text. This is an extremely terrible thing to do. Please hash passwords appropriately so that they can't be read as plain text.
Additionally, you appear to be using a shared connection object:
Connections.con.Open();
This is going to cause a whole host of problems. It's a lot simpler and more stable to create the connection object within the scope of the method which uses it. Basically, a connection object should be created, used, and disposed in a very tight scope and should not leak outside of that scope.
You missed where attribute
string login = "SELECT ID, Username, [Password] FROM Employee where Username=#? and [Password]= #? ";

Calling MySql Function in C# for validating username and password

Development Environment:
Microsoft Visual Studio 2010 Ultimate,
C#,
MySql
Hi, I've create a function in mysql which accept 3 parameter to validate username and password.
DELIMITER $$
USE `generalledger`$$
DROP FUNCTION IF EXISTS `fLogin_Check`$$
CREATE DEFINER=`root`#`localhost`
FUNCTION `fLogin_Check`
(mUserName VARCHAR(50),mUserPass VARCHAR(40),mUserKey VARCHAR(40)) RETURNS INT
BEGIN
DECLARE mCount INT;
SELECT COUNT(*) INTO mCount FROM userMaster
WHERE userName = mUserName
AND AES_DECRYPT(userPass, mUserKey) = UPPER( mUserPass);
IF mCount > 0 THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END$$
DELIMITER ;
As you can see I am using AES_DECRYPT function of MySql to check password, because I've use AES_ENCRYPT for password when INSERT username and password to mysql table.
Now I need to call the function fLogin_Check in C#, which I am doing by using following class method:
public int CheckUser(string mUserName, string mPass, string mKey)
{
oCn = da.GetConnection();
int res;
if (oCn == null)
{
oCn.Open();
}
sInsProcName = "fLogin_Check";
insertcommand = new MySqlCommand(sInsProcName, oCn);
insertcommand.CommandType = CommandType.StoredProcedure;
insertcommand.Parameters.Add(new MySqlParameter("mRes", MySqlDbType.Int32, 0));
insertcommand.Parameters["mRes"].Direction = ParameterDirection.ReturnValue;
insertcommand.Parameters.Add("mUserName", MySqlDbType.VarChar, 50, mUserName);
insertcommand.Parameters.Add("mUserPass", MySqlDbType.VarChar, 40, mPass);
insertcommand.Parameters.Add("mUserKey", MySqlDbType.VarChar, 40);
insertcommand.Parameters["mUserKey"].Value = mKey;
res = insertcommand.ExecuteNonQuery();
//res = int.Parse(insertcommand.Parameters["mRes"].Value.ToString());
return (res);
oCn.Close();
}
oCn is the connection abject which uses to call GetConnection method define in my DAL class and da is the object created from DAL class, use to opening and closing database connection.
Using following Global class I am storing username and password after user enter them, and then try to validating with fLogic_Check Mysql function:
public static class Globals
{
public static string userName;
public static string userPass;
public const string sKey = "AHMEDFINANCEICMAP1122";
}
sKey is the key I use to encrypt password when insert username. Now I am trying to use it in C# from Login Form when user enter Username and Password and click login button with following code:
private void btnCheck_Click(object sender, EventArgs e)
{
Globals.userName = txtUser.Text.ToString();
Globals.userPass = txtPass.Text.ToString();
if (fUser.CheckUser(Globals.userName, Globals.userPass, Globals.sKey) == 0)
{
MessageBox.Show("Invalid Username or Password.");
}
else
{
MessageBox.Show("Login Successfull");
}
}
It always return 0, means failed login. I've checked the Mysql function in MySql GUI and it works fine:
SELECT fLogin_Check("AHMED","AHMED1981","AHMEDFINANCEICMAP1122") FROM userMaster
which successfully return 1, however it fails when calling in C#. I've also tried to access Parameter which I've comment out after failure...What am I doing wrong?
Ahmed
Code has multiple issues.
First and most important: it's better to store passwords with hash functions instead of reversible cryptographyc methods, otherwise you loose the benefits of encrypting them. I'm not going to explain this more in deep because it's been widely discussed, even here on stackoverflow. Do some research.
Second, "ExecuteNonQuery" won't return you the query output. It is used with INSERT's and UPDATE queries. You need to use "ExecuteReader" and then use the object returned to extract the row data.
Finally, your "oCn.Close();" will never be executed since there is a "return" before that line (obiviously that's not the problem but hey!).
Now, since you need the first value of the first row, there is a better function that suits your needs: ExecuteScalar. You can find more information about it on MSDN.

C# - MySQL Database, Check password is correct when it's encrypted?

I'm using a MySQL database with my program and when I check if the password a user enters is correct, it always says it's invalid. I do the same with email but it works. I think it's because my PHP script encrypts the password when it's created on the page. Here is my code:
try
{
string command = "SELECT email FROM uc_users WHERE email = '#email';";
string command2 = "SELECT password FROM uc_users WHERE password = '#password';";
// CONNECTION DETAILS
connection = new MySqlConnection(connectionString);
connection.Open();
// COMMAND DETAILS
MySqlCommand email = new MySqlCommand(command, connection);
MySqlCommand passwordc = new MySqlCommand(command2, connection);
// PARAMETERS
email.Parameters.AddWithValue("#email", txtEmail.Text);
passwordc.Parameters.AddWithValue("#password", txtPassword.Text);
// READER DETAILS
MySqlDataReader dr;
MySqlDataReader dr2;
// CHECK DETAILS
dr = email.ExecuteReader();
string tempE = dr.Read() ? dr.GetString(0) : "Invalid Email";
dr.Close();
dr2 = passwordc.ExecuteReader();
string tempP = dr2.Read() ? dr.GetString(0) : "Invalid Password";
dr2.Close();
MessageBox.Show(tempE + " " + tempP);
if (tempE == txtEmail.Text && tempP == txtPassword.Text)
{
connection.Close();
tempE = "";
tempP = "";
string email2 = txtEmail.Text;
frmAppHub frm = new frmAppHub(email2);
frm.Show();
this.Hide();
}
else
{
MessageBox.Show("Invalid login details. Please try again.");
connection.Close();
tempE = "";
tempP = "";
}
}
catch(MySqlException ex)
{
MessageBox.Show("MySQL Error - Code AHx004: " +ex.Message);
connection.Close();
}
Any ideas how to do it? Any help is appreciated.
The query is fundamentally broken. There should be one query and the approach should be like:
// No quotes around placeholder and only ONE query that does NOT select on the password.
// If the query selects on the password then it means that the password is either
// stored as plaitext (which is not good) or the database value can be computed without
// per-user information (which is also not good).
string command = "SELECT email, password FROM uc_users WHERE email = #email";
// Only look based on the user (the email column should have a Unique constraint)
// as (although a unique salt makes it very unlikely) passwords are not unique
// nor do they identify users.
email.Parameters.AddWithValue("#email", txtEmail.Text);
// Then read the password for the given user
dr = email.ExecuteReader();
if (!dr.Read()) {
// User not found - just stop.
return false;
}
string dbPassword = dr.GetString(1);
return IsPasswordMatch(dbPassword, txtPassword.Text);
Now, IsPasswordMatch is a function which, when, given the database password and the plain-text password, should determine if they are a match by applying all the appropriate hash/salt/whatever transforms (see my first comment). In any case, all the logic can be safely tucked away in there.
It might look something like:
bool IsPasswordMatch (string dbPassword, string plaintext) {
var salt = GetSalt(dbPassword);
var dbHash = GetHash(dbPassword);
var ptHash = Hash(salt + plaintext);
return dbHash == ptHash;
}
I've left in the methods as "high level operations" that need to be adapted to whatever was used in the first place; however, the basic operation should now be apparent. Just repeat the same process as was used to create the database value in the first place - is it the same in both cases?
Make sure to read up on using as well - this will enable resources to be cleaned up easily without fuss.
Yes, you need to generate hash of your password, that must be identical stored in database and than query your command with this hash, instead of plain text password.
If you have different strings in DB and #passowrd - you always have invalid result.

How to check if the username existed in the User table of the database or not?

I am trying to develop a simple user management system for the admin of the web application. I am using ASP.NET Wizard Control for this task.
I just put a TextBox for writing the username and when the admin clicks on the Next button, the system should check if the username existed in the database or not. If it is existed, the system should display his information in a placeholder for the admin.
I am struggling with this task. I did the following in the code-behind:
//For checking the user
if (Request.QueryString["Username"] != null)
{
String strUserName = Request.QueryString["Username"];
//Check userName Here
String strReturnStatus = "false";
if (CheckUsername(Request.QueryString["Username"]) == true)
{
strReturnStatus = "true";
}
Response.Clear();
Response.Write(strReturnStatus);
Response.End();
}
Now, I think to create a second method called CheckUsername which I don't know what I should put it inside it:
private bool CheckUsername(string p)
{
//throw new NotImplementedException();
}
It may seem that this question is simple or stupid, but I am a new developer and I could not be able to find a simple resource that could help me in this issue particularly.
I believe the following method is what you're after:
private bool CheckUsername(string username)
{
string connString = "";
string cmdText = "SELECT COUNT(*) FROM Users WHERE Username = #username";
using(SqlConnection conn = new SqlConnection(connString))
{
conn.Open(); // Open DB connection.
using(SqlCommand cmd = new SqlCommand(cmdText, conn))
{
cmd.Parameters.AddWithValue("#username", username)); // Add the SQL parameter.
int count = (int)cmd.ExecuteScalar();
// True (> 0) when the username exists, false (= 0) when the username does not exist.
return (count > 0);
}
}
}
You can fill in the blanks (e.g specify a connection string connString and modify cmdText). The SQL query I specified in cmdText is under the assumption of a typical user management system where you have common table names and columns; Users (table) and Username (column). It's hard to justify as you haven't specified the structure. Modify it to suit your applications needs.
The method of counting how many records exist is quite common in most cases. I frequently use that method of checking/validating things as I see fit.
Further information about the code (classes) I used in my example above:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.aspx
I also advise you read about data access (not that link). I'll leave you to that.
I adjust some point in your code:
if (!string.IsNullOrEmpty(Request.QueryString["Username"]))
{
---
---
if (CheckUsername(Request.QueryString["Username"]))
{
---
---
}
Refer to this link as tutorial for your task: http://www.codeproject.com/KB/database/sql_in_csharp.aspx

Categories

Resources