I have to make a simple login that will not crash when you insert into the browser a (") so i needed to parameterize the query string but for some reason im gettin an error saying:
Must declare the scalar variable "#UserName"
here is the code
private void DoSqlQuery()
{
try
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["RolaConnectionString"].ConnectionString);
conn.Open();
string checkUser = "select * from UserData where UserName = #UserName";
SqlCommand com = new SqlCommand(checkUser, conn);
com.Parameters.AddWithValue("#Username", txtUserName.Text.Trim());
int temp = Convert.ToInt32(com.ExecuteScalar().ToString());
conn.Close();
if (temp == 1)
{
conn.Open();
string checkPassword = "select Password from UserData where UserName = #UserName";
SqlCommand passConn = new SqlCommand(checkPassword, conn);
com.Parameters.AddWithValue("#Username", txtUserName.Text.Trim());
string password = passConn.ExecuteScalar().ToString();
conn.Close();
if (password == txtPassword.Text)
{
Session["New"] = txtUserName.Text;
Response.Write("Password is correct");
Response.Redirect("~/LoggedIn.aspx");
}
else
{
Response.Write("Password is not correct");
}
}
else
{
Response.Write("Username is not correct");
}
}
catch(Exception e)
{
Response.Write(e.ToString());
}
}
You are referencing the wrong command in the inner if statement:
string checkPassword = "select Password from UserData where UserName = #UserName";
SqlCommand passConn = new SqlCommand(checkPassword, conn);
com.Parameters.AddWithValue("#Username", txtUserName.Text.Trim());
^^^-- should be passConn
As a result, your second command never gets the parameter added so you get the error you mention. Case sensitivity may also be a problem, but it depends on the collation of your database - SQL Server is case-insensitive by default.
Some other suggestions not related to your problem:
Wrap commands and connection in using statements
Query the username and password in one query (WHERE UserName = #UserName AND Password = #Password). Hackers will first search for valid usernames, then try to hack the password using dictionary attacks. Trying to find a matching combination is much harder.
Do not store your passwords in plain text - use a salted hash
Or just use built-in security providers rather than rolling your own.
Related
i'm trying to update my database with a new hashed password on asp.net with a change password form,but it isn't working nor giving me errors.
I'm using bcrypt for hashing.Registration and Login are working just fine,but changing the hashed password is difficult.
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["dbconnection"].ConnectionString);
con.Open();
//Select
string query = "select password from Users where name=#name";
SqlCommand cmd = new SqlCommand(query, con);
cmd.Parameters.AddWithValue("#password", txtOld.Text.Trim());
cmd.Parameters.AddWithValue("#name", LblUser.Text);
//Update
try {
string queryupdate = "UPDATE Users SET password=#newpassword WHERE name=#name";
SqlCommand cmd1 = new SqlCommand(queryupdate, con);
string salt = BCr.BCrypt.GenerateSalt(12);
// if you look at the hashed password, notice that it's prepended with the salt generated above
string hashedPassword = BCr.BCrypt.HashPassword(txtConfirm.Text.Trim(), salt);
cmd1.Parameters.AddWithValue("#name", LblUser.Text);
cmd1.Parameters.AddWithValue("#newpassword", hashedPassword);
cmd1.Parameters.AddWithValue("#password", txtOld.Text.Trim());
cmd1.ExecuteNonQuery();
LblUser.Text = "Password changed successfully";
LblUser.ForeColor = System.Drawing.Color.Green;
}
catch(Exception ex)
{
LblUser.Text = "Something Went Wrong";
LblUser.ForeColor = System.Drawing.Color.Red;
}
I'm assuming you're using the newer version of the bcrypt library https://www.nuget.org/packages/BCrypt.Net-Next/ rather than the old one with bugs.
Firstly don't generate the salt yourself this is dealt with in the library.
You can generate a new password hash securely by simply calling
var myNewHash = BCrypt.ValidateAndReplacePassword(currentPassword, currentHash, newPassword);
This of course forces the process to require the user to enter their current password in order to change their password (this is best practice).
If you're doing this in the sense of a password reset you should hash the password using
var myNewHash = BCrypt.HashPassword("newpassword");
As documented at the start of the readme https://github.com/BcryptNet/bcrypt.net
As for the SQL element; I'd consider using EF or Dapper.Net over direct ADO manipulation. SQL parameterisation isn't foolproof protection against SQLI Examples of SQL injection even when using SQLParameter in .NET?
If you're using ADO though make sure you specify your parameter types ala
var connect = ConfigurationManager.ConnectionStrings["NorthWind"].ToString();
var query = "Select * From Products Where ProductID = #ProductID";
using (var conn = new SqlConnection(connect))
{
using (var cmd = new SqlCommand(query, conn))
{
cmd.Parameters.Add("#ProductID", SqlDbType.Int);
cmd.Parameters["#ProductID"].Value = Convert.ToInt32(Request["ProductID"]);
conn.Open();
conn.Open();
//Process results
}
}
Disclaimer: I'm the author of the listed repo
Code sample for ADO from https://www.mikesdotnetting.com/article/113/preventing-sql-injection-in-asp-net
i had the exact name in my database yet i still keep getting that error as titled.
An exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll but was not handled in user code
Image Link : http://i.imgur.com/tKtvlfj.png
Additional information: Invalid column name 'Username'.
protected void Button1_Click(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ERegistrationConnectionString"].ConnectionString);
conn.Open();
string checkuser = "select count(*)from Employer where Username='" + TextBoxELUsername.Text + "'";
SqlCommand com = new SqlCommand(checkuser, conn);
int temp = Convert.ToInt32(com.ExecuteScalar().ToString());
conn.Close();
if (temp == 1)
{
conn.Open();
string checkPasswordQuery = "select password from Employer where Username='" + TextBoxELUsername.Text + "'";
SqlCommand passComm = new SqlCommand(checkPasswordQuery, conn);
string password = passComm.ExecuteScalar().ToString().Replace(" ","");
if (password == TextBoxLoginPassword.Text)
{
Session["New"] = TextBoxELUsername.Text;
Response.Write("Password is Correct");
}
else
{
Response.Write("Password Incorrect");
}
}
else
{
Response.Write("Username Incorrect");
}
}
Your SQL is invalid. You forgot a space between the count(*) and from keyword. Try this instead:
select count(*) from Employer where Username=
Also you should change your sql to not allow sql injections and use the Parameters object
In the case of your Sql statement to retrieve the count(*) you really should Parameterize that statement to prevent sql injection.
string checkuser = #"select count(*)from Employer where Username= ?";
SqlCommand com = new SqlCommand(checkuser, conn);
comm.Parameters.AddWithValue("?", TextBoxELUsername.Text );
In addition try returning the variable temp in this fashion.
int temp = (int)comm.ExecuteScalar();
Beyond that I would try creating a second connection contained within the IF statement. It may sound odd but that connection can be stripped out of memory before the IF statement is triggered and in turn the program has no idea what connection your are trying to open.
You could avoid a second connection all together by creating a single sql statement
string checkuser = #"select count(*)from Employer where Username= ? and password = ?";
SqlCommand com = new SqlCommand(checkuser, conn);
comm.Parameters.AddWithValue("?", TextBoxELUsername.Text );
comm.Parameters.AddWithValue("?", TextBoxLoginPassword.Text );
your count return will only exist is both the username and password are correct.
You may also want to use the following code to force case sensitivity on your query
alter database your_database collate Latin1_General_CS_AS
I've written this registration form which adds data to my SQL Server database. What I want is an exception when the user enters a username that is already in the database.
protected void Button1_Click(object sender, EventArgs e)
{
try
{
SqlConnection conn2 = new SqlConnection(ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString);
conn2.Open();
string CheckUser = "select Username from UserData where Username like #Username";
SqlCommand com2 = new SqlCommand(CheckUser, conn2);
com2.Parameters.AddWithValue("#Username", "'%"+ UsernameTextBox.Text +"%'");
com2.ExecuteNonQuery();
int IsMatch = Convert.ToInt32(com2.ExecuteScalar().ToString());
conn2.Close();
if (IsMatch == 0)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString);
conn.Open();
string InsertQuery = "insert into UserData (Username, Email, Password, Country) values (#Username, #Email, #Password, #Country)";
SqlCommand com = new SqlCommand(InsertQuery, conn);
com.Parameters.AddWithValue("#Username", UsernameTextBox.Text);
com.Parameters.AddWithValue("#Email", EmailTextBox.Text);
com.Parameters.AddWithValue("#Password", PasswordTextBox.Text);
com.Parameters.AddWithValue("#Country", CountryDropDownList.SelectedItem.ToString());
com.ExecuteNonQuery();
Response.Redirect("Manager.aspx");
conn.Close();
}
else
{
Response.Write("User Already Exists!");
}
}
catch (Exception ex)
{
Response.Write(Convert.ToString(ex));
}
}
When I run it, I get an exception on the following line:
int IsMatch = Convert.ToInt32(com2.ExecuteScalar().ToString());
Blam's second solution works, but the IsMatch can be simplified a bit by casting to int instead of going to string and parsing.
This should also be handled at the database level. Set a primary key on your username column:
ALTER TABLE UserData ADD CONSTRAINT
PK_UserData PRIMARY KEY CLUSTERED (Username)
If you do it this way, then you don't even have to check for duplicates explicitly, you can just try to create the user and handle the exception if it fails:
try
{
using (var conn = new SqlConnection((ConfigurationManager.ConnectionStrings["RegistrationConnectionString"].ConnectionString)))
{
conn.Open();
#if DOUBLE_CHECK
string CheckUser = "select count(*) from UserData where Username = #Username";
SqlCommand com2 = new SqlCommand(CheckUser, conn);
com2.Parameters.AddWithValue("#Username", UsernameTextBox.Text);
if ((int)com2.ExecuteScalar() > 0)
{
Response.Write("User already exists");
return;
}
#endif
string InsertQuerry = "insert into UserData (Username,Email,Password,Country) values (#Username,#Email,#Password,#Country)";
SqlCommand com = new SqlCommand(InsertQuerry, conn);
com.Parameters.AddWithValue("#Username", UsernameTextBox.Text);
com.Parameters.AddWithValue("#Email", EmailTextBox.Text);
com.Parameters.AddWithValue("#Password", PasswordTextBox.Text);
com.Parameters.AddWithValue("#Country", CountryDropDownList.SelectedItem.ToString());
com.ExecuteNonQuery();
Response.Redirect("Manager.aspx");
}
}
catch (SqlException se)
{
if (se.Errors.OfType<SqlError>().Any(e => e.Number == 2627))
{
Response.Write("User already exists");
}
else
{
Response.Write(se.ToString());
}
}
catch (Exception ex)
{
Response.Write(ex.ToString());
}
If you handle the exception this way, the #if DOUBLE_CHECK section is redundant and can be removed. An attempt to add duplicate name will cause a SQL error and exception, and this will detect and handle the "duplicate key" error.
Two unrelated notes on your code:
Response.Redirect() will abort the current thread and your conn.Close() will not be called. Use a using() to ensure it's called.
Storing a password in the database as plain text is a disaster waiting to happen. PLEASE take a look at Best way to store password in database for some ideas about how to do this correctly
That won't return an integer
string CheckUser = "select count(*) from UserData where Username like #Username";
SqlCommand com2 = new SqlCommand(CheckUser, conn2);
com2.Parameters.AddWithValue("#Username", "'%"+ UsernameTextBox.Text +"%'");
int IsMatch = Convert.ToInt32(com2.ExecuteScalar().ToString());
And you don't need to use two different connections.
Just use one and close it in a Finally.
string CheckUser = "select count(*) from UserData where Username = #Username";
SqlCommand com2 = new SqlCommand(CheckUser, conn2);
com2.Parameters.AddWithValue("#Username", UsernameTextBox.Text );
int IsMatch = Convert.ToInt32(com2.ExecuteScalar().ToString());
This returns 0 or 1. This should fix your issue. Looks like you need to return an int type. Or you could change it to bool if you want. Either way, this sql statement should help! :)
select
isnull(convert(bit,(select top 1 case
when username != '' then 1
else 0 end
from UserData
where username like #Username)),0)
I want to create a Registration and Log In form on Visual Studio 2010 (with Visual C#).
I have created Service-Based Database and one table. I can insert data into the table (at the registration form), but I cannot figure out how to log in the user.
I have a very simple Log In Form (just fields for username and password) and a 'Log In' Button. I do not really know how to check if the password and the username (that exist in my database) match. Here is what I have so far:
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text != "" & textBox2.Text != "")
{
cn.Open(); // cn is the Sqlconnection
cmd.Parameters.AddWithValue("#Username", textBox1.Text); // cmd is SqlCommand
cmd.Parameters.AddWithValue("#Password", textBox2.Text);
if (cmd.CommandText == "SELECT * FROM Table1 WHERE username = #Username AND password = #Password")
{
MessageBox.Show("Loggen In!");
this.Close();
}
cn.Close();
}
}
You need to Execute the query to know if the information exists in the database
if (textBox1.Text != "" & textBox2.Text != "")
{
string queryText = #"SELECT Count(*) FROM Table1
WHERE username = #Username AND password = #Password";
using(SqlConnection cn = new SqlConnection("your_connection_string"))
using(SqlCommand cmd = new SqlCommand(queryText, cn))
{
cn.Open();
cmd.Parameters.AddWithValue("#Username", textBox1.Text);
cmd.Parameters.AddWithValue("#Password", textBox2.Text);
int result = (int)cmd.ExecuteScalar();
if (result > 0)
MessageBox.Show("Loggen In!");
else
MessageBox.Show("User Not Found!");
}
}
I have also changed something in your code.
Changed the query text to return just the count of the users with the
specific username and account and be able to use ExecuteScalar
Enclosed the creation of the SqlConnection and SqlCommand in a using
statement to be sure to dispose these objects at the end of the
operation
I also recommend to change the the way in which you store the password.
Store, in the password field, an hash not the clear password. Then pass to the database the same hash and compare this against the content of the database field.
In this way, the password is known only to your user, not by you or by any passersby that looks at the database table
SqlConnection con = new SqlConnection("connection_string");
SqlCommand cmd = new SqlCommand("select Count(*) from [dbo].[Table] where uname=#uname and password=#password");
cmd.Connection = con;
con.Open();
cmd.Parameters.AddWithValue("#uname", uname.Text);
cmd.Parameters.AddWithValue("#password", password.Text);
int Result=(int)cmd.ExecuteScalar();
if (Result > 0)
{
Response.Redirect("welcome.aspx");
}
else
{
Response.Redirect("register.aspx");
}
I have the following code in my btn_click event:
Sqlconnection con = new Sqlconnection("server=.;database=bss;user id=ab;pwd=ab");
con.open();
SqlCommand cmd = new Sqlcommand("select * from login where username='"
+ txt4name.Text + "' and pwd='" + txt4pwd.Text + "'", con);
SqlDataReader reader = cmd.execute Reader();
Where login is the table and username and pwd are its fields. After this code all the values are stored in the reader object. I want to store username and pwd in the separate variables.
How can I accomplish this?
In general, when accessing your DB, you should be using something similar to this instead to eliminate SQL injection vulnerabilities:
using (SqlCommand myCommand = new SqlCommand("SELECT * FROM USERS WHERE USERNAME=#username AND PASSWORD=HASHBYTES('SHA1', #password)", myConnection))
{
myCommand.Parameters.AddWithValue("#username", user);
myCommand.Parameters.AddWithValue("#password", pass);
myConnection.Open();
SqlDataReader myReader = myCommand.ExecuteReader())
...................
}
But more realistically to store credentials, you should be using something like the Membership system instead of rolling your own.
You're running a huge risk of sql injection with that. Use SQL Parameters for values into SqlCommands.
If you mean c# variables, and if you want to get them from db, just do this:
SqlDataReader reader = cmd.execute Reader();
if (reader.Read())
{
string username = reader["username"];
string pwd = reader["password"];
}
While you are at it, parameterize your query and prevent sql injection:
SqlCommand cmd = new Sqlcommand("select * from login where username=#username and pwd=#pwd", con);
cmd.Parameters.AddWithValue("#username", txt4name.Text);
cmd.Parameters.AddWithValue("#pwd", txt4pwd.Text);
Definitely heed the advice about SQL injection but here is the answer to your question:
String username;
String pwd;
int columnIndex = reader.GetOrdinal("username");
if (!dataReader.IsDBNull(columnIndex))
{
username = dataReader.GetString(columnIndex);
}
columnIndex = reader.GetOrdinal("pwd");
if (!dataReader.IsDBNull(columnIndex))
{
pwd = dataReader.GetString(columnIndex);
}
string userName = txt4name.Text;
string password = txt4pwd.Text;
Is that really what you want? Just to get that data into variables?
You really need to use parameterized SQL. There's an example here
Furthermore, your question doesn't really make sense; you want the username and password in seperate variables? they already are seperate in your example. If you are unable to assign them to strings I suggest following some tutorials.
Another approach is to load the reader results into a DataTable like so:
DataTable Result = new DataTable();
Result.Load(reader);
If your login table only contains two columns (userName and password) that are unique you end up with Result containing only one row with the information. You can then get the column values from each column:
string userName = Result.Rows[0].Field<string>("userName");
string password = Result.Rows[0].Field<string>("pwd");
private void but_login_Click(object sender, EventArgs e)
{
string cn = "Data Source=.;Initial Catalog=mvrdatabase;Integrated Security=True";
SqlConnection con = new SqlConnection(cn);
con.Open();
SqlCommand cmd = new SqlCommand("select count (*) from logintable where username ='" + txt_uname.Text + "'and password='" + txt_pass.Text + "'", con);
int i = Convert.ToInt32(cmd.ExecuteScalar());
con.Close();
if (i == 1)
{
Form2 f2 = new Form2();
MessageBox.Show("User login successfully........");
this.Hide();
f2.Show();
}
else
{
MessageBox.Show("INCORRECT USERID AND PASSWORD", "Error");
}
}
You can usually find basic usage examples on MSDN, like this one for SqlDataReader.