Retrieving user password from DB - c#

I have a table that has a list of user names and passwords and I want to match the the user name and password the user enter with the one saved in the DB. The code I am using only retrieves the login information of the first record in the users table, how can I change that so the code would work for all user.
I am a beginners at VS and I am trying to learn the basics so later on I will be implementing more complex login page with encryption.. Thank you
private void btnLogin_Click(object sender, EventArgs e)
{
SqlConnection cn = new SqlConnection();
SqlCommand cmd = new SqlCommand();
SqlDataReader dr;
cn.ConnectionString = "Server=;Database=;User Id=naljalid;Password=";
cmd.Connection = cn;
string username = tbxUserName.Text;
cmd.CommandText = "SELECT UserPassword FROM tblLoginProject WHERE UserName=username";
// open a connection to DB
cn.Open();
//read the table
dr = cmd.ExecuteReader();
//read a record from te data reader
dr.Read();
// compare the passwords
if (tbxPassword.Text == dr.GetString(0))
{
MessageBox.Show("Hello");
}
else
{
MessageBox.Show("Login failed, please re-enter your password");
}
}

The key to this is the SQL query, specifically the WHERE clause:
SELECT UserPassword FROM tblLoginProject
This query will return all the passwords from the database. But you want to retrieve the password of only one user, so you need to implement a WHERE clause
SELECT UserPassword FROM tblLoginProject WHERE UserName = #username
This query will retrieve the password for only a certain user where the value of the field UserName equals the value passed in the parameter #username. So now we need to make sure to pass that value. You can't just include it in the SQL query like you're doing right now. We do it like this:
cmd.Paramateres.AddWithValue("#username", username);
This should work fine, but for best practices you should check for both username and password at the same time:
SELECT count(*) FROM tblLoginProject WHERE UserName = #username AND UserPassword = #password
Then of course we pass both values:
cmd.Paramateres.AddWithValue("#username", username);
cmd.Paramateres.AddWithValue("#password", password);
This will return 0 if no users are found with that combination of username and password (invalid login), or more than 0 if such a user was found (valid login).
Next stop you should research hashing passwords. After that would be salting these hashes. Good luck!

Change the query a tidge to this:
cmd.CommandText = "SELECT UserPassword FROM tblLoginProject WHERE UserName = #username";
and then set that parameter value:
cmd.Parameters.AddWithValue("#username", tbxUserName.Text);
That will get you the row for the user you're looking for. Now on to a few more recommendations. The ADO.NET classes implement the IDispoable interface. That interface identifies that the class uses some unmanaged resources. You want to make sure those get disposed. Consider the following rewrite of your current code:
using (SqlConnection cn = new SqlConnection("Server=;Database=;User Id=naljalid;Password="))
using (SqlCommand cmd = new SqlCommand("SELECT UserName FROM tblLoginProject WHERE UserName = #username AND Password = #password", cn))
{
cn.Open();
cmd.Parameters.AddWithValue("#username", tbxUserName.Text);
cmd.Parameters.AddWithValue("#password", tbxPassword.Text);
var result = cmd.ExecuteScalar() as string;
if (string.IsNullOrEmpty(result))
{
// user was not found
}
else
{
// user was found
}
}
It leverages the using statement to ensure that the objects get disposed.

Related

Parameterizing SQL command to change Contained DB user's password

I have some Contained DB users in SQL Server 2017. In C# I am trying to execute a SQL command to change the password of a particular one of these users. The DB connection has permission to do this and does not require me to know the current password of the user in order to do so.
var sqlStatement = $"ALTER USER {userName} WITH PASSWORD = '#Password';";
using (var cmd = new SqlCommand(sql, sqlConnection))
{
cmd.CommandType = CommandType.Text;
//cmd.Parameters.Add("#UserName", SqlDbType.NVarChar).Value = userName;
cmd.Parameters.Add("#Password", SqlDbType.NVarChar).Value = password;
var rowsAffected = cmd.ExecuteNonQuery();
}
Above is the code I had been using. Running a trace on what was produced shows the command to be:
exec sp_executesql N'ALTER USER UserName1 WITH PASSWORD = ''#Password'';',N'#Password nvarchar(18)',#Password=N'Hqc5w7m98s4J!9'
This does not error at all, in fact it seems to work. But when I attempt to login (before the password change I could log in fine) I cannot do so. It appears to have changed the password but not to the one I specified. If I run the following command instead (no parameters in C#, just string building):
ALTER USER [UserName1] WITH PASSWORD = 'Hqc5w7m98s4J!291'
Then I am now able to login again with the new password. I cannot seem to find out what is happening in the first case? Why is it not changing the password as I expect? I've tried a few variations of it to try and get it to work using parameters but no luck. I don't want to just use the parameterless version of course but have spent around 5 hours looking at this already.
Alter User may not work with traditional parameterization.
string sqlStatement = #"
DECLARE #sql NVARCHAR(500)
SET #sql = 'ALTER USER UserName1 WITH PASSWORD= ' + QuoteName(#Password, '''')
EXEC(#sql)";
using (var cmd = new SqlCommand(sqlStatement , sqlConnection))
{
cmd.Parameters.Add("#Password", SqlDbType.NVarChar).Value = password;
// execute...
}

ASP.NET Not sure if reading from SQL Server database correctly

I am trying to make a user register page that uploads the user data to a sql server database. I want to have the capability to check if a username already exists and prevent it from being made. I am able to create a new user with first name, last name, username, etc and it updates the database, but it doesn't stop me from creating a user with a username that already exists in the database. Here is my code:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
//connect registration form to database
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["sandboxConnectionStringUserData"].ConnectionString);
conn.Open(); //open connection
//SQL Query
string checkUserName = "select count(*) from UserData where UserName='"+TextBoxUserName.Text+"'";//checks if username is already there
SqlCommand command = new SqlCommand(checkUserName, conn);
int temp = Convert.ToInt32(command.ExecuteScalar().ToString());
if(temp == 1)
{
Response.Write("User name already exists");
}
conn.Close(); //close the database
}
}
I tried debugging and temp's value never changes to 1.
Thanks!
Just add a UNIQUE constraint to the username column and handle the sql exception in your app. Additionally you can/should write an SP that takes username as an argument and checks for existence in the table, and use that in your server-side validation after the form is sorted but before the insert (save()) occurs. That way you reduce the probability of encountering a sql exception but can still deal with it if it occurs.
Your current method of appending the form data to a raw sql query is going to open you up to sql injection. Use a parameterized query instead.
Creating a UNIQUE constraint on the UserName column is a good start. I would also create a stored procedure that checks the existence of the user and inserts or updates as well structure your code a bit more efficiently. The username should be passed in as a parameter and you should properly dispose of the connection object.
As an example your stored procedure may look like:
CREATE PROCEDURE dbo.uspUserData #userName VARCHAR(50)
AS
BEGIN
IF EXISTS(SELECT 1 FROM dbo.UserData WITH(NOLOCK)
WHERE UserName = #userName)
BEGIN
-- update
END
ELSE
BEGIN
-- insert
END
END
And your .NET code may look like:
using (
SqlConnection conn =
new SqlConnection(
ConfigurationManager.ConnectionStrings["sandboxConnectionStringUserData"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("uspUserData", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#UserName", SqlDbType.VarChar).Value = TextBoxUserName.Text;
conn.Open();
cmd.ExecuteNonQuery();
}
}

Using Parameters.AddWithValue to pass database name fails

I've been trying to perform a login query. I think my main problem with this function is the Parameters.AddWithValue portion, but don't really understand what is wrong.
Following code returns an error when ran:
Must declare the table variable "#database"
Code:
public static bool clsFuncLogin(string USER, string PASS,
string conStr, string strDatabase)
{
SqlConnection conn = new SqlConnection(
ConfigurationManager.ConnectionStrings[conStr].ConnectionString);
conn.Open();
using (SqlCommand StrQuer =
new SqlCommand("SELECT COUNT(*) FROM #database "+
"WHERE Username = #userid AND Password = #password", conn))
{
StrQuer.Parameters.AddWithValue("#userid", USER);
StrQuer.Parameters.AddWithValue("#password", PASS);
StrQuer.Parameters.AddWithValue("#database", strDatabase);
int DataQuery = Convert.ToInt32(StrQuer.ExecuteScalar().ToString());
if (DataQuery == 1)
{
System.Web.HttpContext.Current.Session["User"] = USER;
System.Web.HttpContext.Current.Session["Pass"] = PASS;
System.Web.HttpContext.Current.Session["loggedIn"] = "True";
return true;
}
else if (DataQuery > 1)
{
//to tell if a double is created in the db
//probably to be removed
System.Web.HttpContext.Current.Session["Double"] = USER;
return false;
}
else
{
return false;
}
}
}
Ive also done the query as
"SELECT COUNT(*) FROM" + strDatabase + " WHERE Username = " + USER +
" AND Password = " + PASS;
but I was told that that is bad practice. Any advice?
I've never seen table names passed as a parameter, and based on other posts (this and this for example), I don't think it can be easily done... at least, not via SqlCommand.Parameters.
It sounds like there's only two tables - admins and regular users. As an alternative, you could just pass a bool to the method, like isAdmin, then have two queries based on whether the user is an admin or a regular user.
public static bool clsFuncLogin(string user, string pass, string conStr, bool isAdmin)
{
...
var query = isAdmin
? "SELECT COUNT(*) FROM ADMIN_TABLE WHERE Username = #userid AND Password = #password"
: "SELECT COUNT(*) FROM REGULAR_TABLE WHERE Username = #userid AND Password = #password";
using (var sqlCommand = new SqlCommand(query, conn))
{
sqlCommand.Parameters.AddWithValue("#userid", user);
sqlCommand.Parameters.AddWithValue("#password", pass);
...
...
Main reason to use
SELECT * FROM TABLE WHERE column=#column
with parameters is to avoid all complications of providing correct formatting for the value. it is even more important if values are supplied as free text by user of your application since you will open all possibilities with SQL injection attacks if you fail to properly address all formatting issues (which is basically not worth the effort given solution with parameters).
I really hope that you don't allow users to supply table name and it's supplied by your own code, so you can quite safely use
var query = String.Format("SELECT * FROM {0} WHERE column=#column", tableName);
However I would just recommend to create two separate queries instead to properly separate concerns.

COM object that has been separated from its underlying RCW cannot be used?

Setting aside the salt and hash debate. And Please reply only if you know the answer.
I am trying to create a method where a user enters their credentials with date and times are recorded automatically when logging in and out.
I have two problems
Problem 1 -
I have created a simple method just for logging in and out. When I included the date and time code I noted that these where recorded and stored for all users. I currently have two users. So if one user logins date and time are recorded and stamp for the other user.
Problem 2 -
The second problem is as the subject headers says I get a error message when the Update command parameter is in the same method as with Select.
If anyone could help me I would be grateful with both of the problems. Hopefully It is only a minor issue? If omitting date and time then I will be grateful if someone could help me on multi login function.
Access 2003 ~ Two tables. Table 1 - Named LoginTable Table 2 - Named LoginLogTable
LoginTable
FieldName DataType
UserName Text
Password Text
LoginLogTable
FieldName DataType
UserNameID Text
UserName Text
LoggedIn Date/Time
LoggedInTime Date/Time
private void btnLogin_Click(object sender, EventArgs e)
{
using (var command = myCon.CreateCommand())
{
command.CommandText = "select UserName, Password from LoginTable where WHERE STRCOMP(UserName, #UserName,0) = 0 AND STRCOMP(Password, #Password,0)=0";
command.Parameters.AddWithValue("UserName", (txtUserName.Text));
command.Parameters.AddWithValue("Password", (txtPassword.Text));
myCon.Open();
var reader = command.ExecuteReader();
{
if (reader.HasRows)
{
MessageBox.Show("Login Successful");
Form2 frm = new Form2();
frm.Show();
while (reader.Read())
{
txtUserName.Text = reader["UserName"].ToString();
txtPassword.Text = reader["Password"].ToString();
}
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = myCon;
cmd.CommandText = "UPDATE [LoginLogTable] SET [LoggedInDate] = ?, [LoggedInTime] = ?";
cmd.Parameters.AddWithValue("#LoggedInDate", DateTime.Now.ToShortDateString());
cmd.Parameters.AddWithValue("#LoggedInTime", DateTime.Now.ToString("HH:mm"));
cmd.ExecuteNonQuery();
myCon.Close();
}
else MessageBox.Show("Login Falied");
}
}
myCon.Close();
myCon.Close();
}
You don't have any condition in your update query, so it will update all records in the table. Add a condition to only update a single record. I don't know what you have in your table, but something like this:
cmd.CommandText = "UPDATE [LoginLogTable] SET [LoggedInDate] = ?, [LoggedInTime] = ? where UserName = ?";
cmd.Parameters.AddWithValue("LoggedInDate", DateTime.Now.ToShortDateString());
cmd.Parameters.AddWithValue("LoggedInTime", DateTime.Now.ToString("HH:mm"));
cmd.Parameters.AddWithValue("UserName", txtUserName.Text);
You should close and dispose the first data reader and command before executing the second command.
I suppose you need a UserName column in your LoginLogTable and update table something like this
UPDATE [LoginLogTable] SET [LoggedInDate] = ?, [LoggedInTime] = ? WHERE UserName = 'YourUserName'.
And the second, I believe you no need a Reader here. You can use ExecuteScalar instead Reader. The second command can not run because a Reader has open state.

retrieve a field from sql through login

I've created 2 tables in a database, each table has a CompanyID field that's in relationship with eachother
In my registration page the company has to register the following info:
CompanyID
Company Name
Business Type
Contact Person
Telephone
Address
e-mail
Password
The company then logs in using their Company Name and Password, once logged in a company can post a job and the problem I have is in my jobtable there is also a field called CompanyID
How can I retrieve the CompanyID from the already registered companys stored in my Company table and use it
SqlParameter p6 = new SqlParameter("CompanyID", 'here' );
my full code:
protected void Button1_Click(object sender, EventArgs e)
{
string strcon = "Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\VC_temps.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
SqlConnection con = new SqlConnection(strcon);
SqlCommand com = new SqlCommand("Store-Jobs", con);
com.CommandType = CommandType.StoredProcedure;
SqlParameter p1 = new SqlParameter("Job", TextBox1.Text);
SqlParameter p2 = new SqlParameter("JobType", DropDownList1.Text);
SqlParameter p3 = new SqlParameter("StartDate", TextBox3.Text);
SqlParameter p4 = new SqlParameter("Time", TextBox2.Text);
SqlParameter p5 = new SqlParameter("JobID", TextBox1.Text.Substring(3).ToUpper());
SqlParameter p6 = new SqlParameter("CompanyID", );
SqlParameter p7 = new SqlParameter("PositionFilled", "NO");
SqlParameter p8 = new SqlParameter("Description", TextBox4.Text);
com.Parameters.Add(p1);
com.Parameters.Add(p2);
com.Parameters.Add(p3);
com.Parameters.Add(p4);
com.Parameters.Add(p5);
com.Parameters.Add(p6);
com.Parameters.Add(p7);
com.Parameters.Add(p8);
con.Open();
com.ExecuteNonQuery();
Labelinfo.Text = "Post successful.";
}
There are several ways to do this. Since the user is logging in to a specific company then I am assuming the CompanyId would be the same during the entire session. Therefore I would recommend retrieving the CompanyId during the login process and storing it into the Session variable. Then you get the CompanyId from this session variable anytime you wish.
An alternative would be to pass the company name into the stored procedure and let the stored procedure get the CompanyId using the Company Name value.
On a secondary note....I like to show best practices when I see code that could have problems. Your code will work fine if everything succeeds but if exceptions occur then you will leave SQL connections open. I have taken the liberty to refactor your code to show an alternative...
string strcon = "Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\VC_temps.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True";
// Use "using" statement to guarantee connection is closed even if exception occurs
using (SqlConnection con = new SqlConnection(strcon))
{
con.Open();
using (SqlCommand com = con.CreateCommand("Store-Jobs"))
{
com.CommandType = CommandType.StoredProcedure;
com.Parameters.AddWithValue("Job", TextBox1.Text);
com.Parameters.AddWithValue("JobType", DropDownList1.Text);
com.Parameters.AddWithValue("StartDate", TextBox3.Text);
com.Parameters.AddWithValue("Time", TextBox2.Text);
com.Parameters.AddWithValue("JobID", TextBox1.Text.Substring(3).ToUpper());
com.Parameters.AddWithValue("CompanyID", );
com.Parameters.AddWithValue("PositionFilled", "NO");
com.Parameters.AddWithValue("Description", TextBox4.Text);
com.ExecuteNonQuery();
}
}
Labelinfo.Text = "Post successful.";
You have a couple of options:
You can use the company name to retrieve the company id from the Company table, using a different stored procedure, and then call the Store-Job procedure as you do in your code.
You can modify Store-Job to take the company name rather than the company id, and, within that procedure, look up the company id by company name and then use it for performing an Insert on teh Jobs table.
Since you have some form of verification going on at login, that might be a good time to retrieve the company id and store it in session (as Gene S suggests above). You can then retrieve it and use it in your stored procedure.
Since you will probably need to use it in many places in your app, to ensure that the data being saved or retrieved belongs to the right company, option 3 sounds like the best.

Categories

Resources