Validating username and password in a database in asp.net - c#

I am trying to create a login page. I have a database table called Login, and it has two columns: ID and Password. It has the following ID and Password pairs in it: First row:(13282,123456), Second Row:(11111,11111). If username and password is right, i redirect page to succesful.aspx, if either username or password is wrong, i redirect page to unsuccesful.aspx. My problem is, When i enter 13283 as ID and 123456 as password, it does everything right, i am redirected to succesful page. But when i enter ID=11111 and Password=11111 even though everything is true, it redirects to unsuccesful page. I think the problem is, my query only checks the first row. Here is the code:
protected void loginButton_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection();
con.ConnectionString = "Data Source=.\\SQLEXPRESS;Initial Catalog=University;Integrated Security=True;Pooling=False";
Int32 verify;
string query1 = "Select count(*) from Login where ID='" + idBox.Text + "' and Password='" + passwordBox.Text + "' ";
SqlCommand cmd1 = new SqlCommand(query1, con);
con.Open();
verify = Convert.ToInt32(cmd1.ExecuteScalar());
con.Close();
if (verify > 0)
{
Response.Redirect("succesful.aspx");
}
else
{
Response.Redirect("unsuccesful.aspx",true);
}
}

Several things are wrong with this approach:
It requires storing passwords in plain text - This is the worst thing one can do to a user's password: anyone who accidentally gains access to your database would instantly be in possession of all your users' passwords, with is very, very bad.
It is susceptible to SQL Injection attacks - Concatenating strings to produce a SQL command is dangerous, because malicious users could enter strings that break your SQL and turn it into something else.
You should study the answers to this question. The approaches discussed there are not nearly as simple as what you are implementing, but they make your system a lot more bullet-proof.

Related

ADODB.Command, finding rows that satisfy a condition

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.

asp.net MVC. How ho make redirect to .aspx page depending on user privilege [duplicate]

This question already has answers here:
ASP.NET Identity 2.0 check if current user is in role IsInRole
(4 answers)
Unable to configure AspNet.Identity using PostgreSQL
(2 answers)
Closed 4 years ago.
I have a little web application on asp.net MVC + PostgreSQL.
I have login page, where people enters their login/password. Right now I only have 1 user - admin. If login and pass are correct, I enter to mainForm.aspx page.
But I need to make a couple of users: user and director. When user logs in, he needs to be redirected to user.aspx page, when director logs in, he needs to be redirected to director.aspx page. All cases need to make login/pass check from PostgreSQL database.
How do I do that?
Here's my Login.aspx.cs code with only 1 user:
namespace User1.WebApp
{
public partial class Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.UnobtrusiveValidationMode = System.Web.UI.UnobtrusiveValidationMode.None;
}
protected void Button_Login_Click(object sender, EventArgs e)
{
var connString = "Host=localhost;Username=postgres;Password=123;Database=postgres";
using (var conn = new NpgsqlConnection(connString))
{
conn.Open();
string checkuser = "select count(*) from Login where name= '" + TextBoxUserName.Text + "' ";
NpgsqlCommand com = new NpgsqlCommand(checkuser, conn);
int temp = Convert.ToInt32(com.ExecuteScalar().ToString());
if (temp == 1)
{
string checkPasswordQuery = "select password from Login where name= '" + TextBoxUserName.Text + "'";
NpgsqlCommand passCom = new NpgsqlCommand(checkPasswordQuery, conn);
string password = passCom.ExecuteScalar().ToString().Replace(" ", "");
if (password == TextBoxPassword.Text)
{
Session["New"] = TextBoxUserName.Text;
Response.Redirect("MainForm.aspx");
}
else
{
Response.Write("Password is NOT correct !");
}
}
else
{
Response.Write("Username is NOT correct !");
}
}
}
}
}
You could do this just before
Response.Redirect("MainForm.aspx");
The way you can do it is to check the type of user and act accordingly.
Few comments regarding the current code:
Set the connection string in web.config and read it from there instead of having it hard coded in your code e.g. here.
The way you create your SQL statements makes your application vulnerable to SQL injection, one of the most common ways for someone to hack a site. Instead of doing this, prefer parameterized queries.
You make two round trips to the database, to check if the user exists and then to get her password. What about if you want to fetch one more information like the user type ? You would make one more round trip. You could eliminate all this to one round trip, provided that you can identify your users based on something unique like the username. Just fetch all the data for a specific username.
Let that someone can get access to the Login table of your database. How exposed are your application users ? 100%. All the passwords there are in clear text ! You should avoid this in any way. A naive solution is to hash the password and each time someone try to login to hash the password that the user provides and compare with the hash you have stored. A more professional approach of storing passwords is described at The right way to implement password hashing using PBKDF2 and C#. Look also for similar articles like the mentioned one. Security should be of paramount importance for your applications.
Are you able to store an extra field in the database to specify whether a login is Admin, User or Director?
You could use an Enum for this:
enum LoginRole
{
User = 0,
Director = 1,
Admin = 2
}
This Enum could be stored as an integer field in your Login table, called "role" or similar. You could then redirect to the appropriate page depending on this role.
I have updated your code with an example:
var connString = "Host=localhost;Username=postgres;Password=123;Database=postgres";
using (var conn = new NpgsqlConnection(connString))
{
conn.Open();
string checkuser = "select password, role from Login where name=#username";
using (var com = new NpgsqlCommand(checkuser, conn))
{
com.Parameters.AddWithValue("#username", TextBoxUserName.Text);
using (var reader = com.ExecuteReader())
{
if (reader.Read())
{
string password = reader["password"].ToString();
LoginRole role = (LoginRole)reader["role"];
if (password == TextBoxPassword.Text)
{
Session["New"] = TextBoxUserName.Text;
switch (role)
{
case LoginRole.User:
Response.Redirect("user.aspx");
break;
case LoginRole.Admin:
Response.Redirect("MainForm.aspx");
break;
case LoginRole.Director:
Response.Redirect("director.aspx");
break;
}
}
else
Response.Write("Password is NOT correct !");
}
else
Response.Write("Username is NOT correct !");
}
}
}
Please note, using parameters in your queries in this case would be preferred, as appending the string from the textbox straight into the SQL query is vulnerable to SQL injection.
You were also making two calls to the database per login - one to check the username and another to check the password. I have addressed this in my above sample to only make one call to the database.

Trying to create a log in page

I am trying to create a login page where you would enter in a username and a password. It will query the database for the information you typed in, and if it is in the database, it will log me into the program. If not, it will display a message saying information is not correct.
Here is what I have so far.
private void okButton_Click(object sender, RoutedEventArgs e)
{
try
{
SqlConnection UGIcon = new SqlConnection();
UGIcon.ConnectionString = "XXXXXXXXX; Database=XXXXXXXX; User Id=XXXXXXX; password=XXXXXXXXX";
UGIcon.Open();
SqlCommand cmd = new SqlCommand("SELECT User(Username, '') AS Username, User(Password,'') AS Password, FROM User WHERE Username='"
+ txtUsername.Text + "' and Password='" + txtPassword.Password + "'", UGIcon);
SqlDataReader dr = cmd.ExecuteReader();
string userText = txtUsername.Text;
string passText = txtPassword.Password;
while (dr.Read())
{
if (this.userText(dr["stUsername"].ToString(), userText) &&
this.passText(dr["stPassword"].ToString(), passText))
{
MessageBox.Show("OK");
}
else
{
MessageBox.Show("Error");
}
}
dr.Close();
UGIcon.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
But, the only problem is it does not work at all. I am not sure I have the correct statements to query the database either. I am also getting an error on the "this.userText" As well.
{
if (this.userText(dr["stUsername"].ToString(), userText) &&
this.passText(dr["stPassword"].ToString(), passText))
{
For the error I'm getting, it tells me the WPF does not contain a definition for it
I am a little unsure of how to fix it and go about it as this is the first time I've had to do this. But I think I have a decent start to it though.
There are a couple of things wrong with this structure:
this.userText(dr["stUsername"].ToString(), userText)
First, userText isn't a function, it's a local variable. So I'm not sure what you're even trying to do by invoking it as a function. Are you just trying to compare the variable? Something like this?:
this.userText.Equals(dr["stUsername"].ToString())
Second, the error is telling you that the object doesn't contain a definition for userText because, well, it doesn't. When you do this:
this.userText
you're specifically looking for a class-level member called userText on the object itself. But your variable is local to the function:
string userText = txtUsername.Text;
So just drop the this reference:
userText.Equals(dr["stUsername"].ToString())
Third, the column reference is incorrect. Note how you define the columns in your SQL query:
SELECT User(Username, '') AS Username, User(Password,'') AS Password ...
The column is called Username, not stUsername:
userText.Equals(dr["Username"].ToString())
Edit: #Blam made a good point in a comment, which demonstrates a logical error in the code. If no results are returned from your query, the while loop will never execute. So no message will be shown. You can check for results with something like HasRows:
if (dr.HasRows)
MessageBox.Show("OK");
else
MessageBox.Show("Error");
This kind of renders the previous things moot, of course. But it's still good to know what the problems were and how to correct them, so I'll leave the answer whole for the sake of completeness regarding the overall question.
A few other notes which are important but not immediately related to your question...
Your code is vulnerable to SQL injection attacks. You'll want to look into using parameterized queries instead of concatenating string values like that. Essentially what this code does is treat user input as executable code on the database, allowing users to write their own code for your application.
Please don't store user passwords in plain text. The importance of this can not be overstated. The original text of a password should never be readable from storage. Instead, store a hash of the password. There's a lot more to read on the subject.
Look into using blocks to dispose of resources when you're done with them.
SqlCommand cmd = new SqlCommand("SELECT count(*) FROM User WHERE Username='"
+ txtUsername.Text + "' and Password='" + txtPassword.Password + "'", UGIcon);
Int32 rowsRet = (Int32)cmd.ExecuteScalar();
if(rowsRet > 0)
{
MessageBox.Show("OK");
}
else
{
MessageBox.Show("Error");
}
You still have exposure to SQL injection attack.

Basic Login page just to retrieve User's info

I am using ASP.NET membership because this is not typical website to have that kind of authentication. I have table that Admin assigns with Uname and Password (Encrypted).
I have a LogIn page when clicked on Login button I need to search for the user in my DB table.
public void getuserinfo()
{
String var = System.Configuration.ConfigurationManager
.ConnectionStrings["KKSTechConnectionString"].ConnectionString;
SqlConnection conn = new SqlConnection(var);
SqlCommand myCmd = new SqlCommand("SELECT Username, Pass FROM Users", conn);
conn.Open();
if(TextBox1== Username) && (TextBox2== Password) <- I am not able to get this right :(
{
//How do I get his info??
}
}
*Database table for dbo.users:*
Uname(PK) | Pass | EmpID (FK)
I want to first decrypt the password and then compare it with the TextBoxPassword..
Please help.
Another approach will be to hash the password entered at user screen and compare that with the hashed password stored at the database.
Now, use your function like this.
public void ValidateUser()
{
var connectionString = System.Configuration.ConfigurationManager
.ConnectionStrings["KKSTechConnectionString"].ConnectionString;
var userName = txtUserName.Text;
var hashedPassword = Helper.ComputeHash(txtPassword.Text, "SHA512", null);
// this query should be parameterised when used in production to avoid SQL injection attacks
var query = String.Format("SELECT Username, Pass FROM Users WHERE Username='{0}' AND Pass='{1}'",
userName,
hashedPassword);
using(var connection = new SqlConnection( connectionString ))
using(var command = new SqlCommand(query, connection ))
{
conn.Open();
SqlDataReader reader=command.ExecuteReader();
if(reader.Read())
{
}
reader.Close();
}
}
Few questions before we get to solution:
How are you sending username and password from client to server side? Is it plain text?
Where does encryption happens?
Have you written encryption code or using one of the standards available?
In your code, I can notice following issues:
You have created the connection object and command object although you have never executed the query on the database.
The query you are using return entire user table which is definitely not required. You already have the user inputs for username and password so just find if they are correct.
Make your query parameterized and send the input to database. There, check if the username and password match. If they do, send back user details (which you will need somewhere in the application, I suppose) or else either, throw exception or return null.
Make sure to take care of encryption/decryption before matching username and password.

How to open make a link open in new tab?

how to open the btnSMS.PostBackUrl with a new tab / new window? Or is there anyway to go to that URL then automatically go to another page?
More information: That URL is a method to send SMS using service provider (Clickatell). But the URL got nothing other than stating if the message send was successful or not. I don't want the user who use my ASP.NET to stuck there. I want to allow them to go back to my website after sending the message.
protected void btnSMS_Click1(object sender, EventArgs e)
{
String HP = txtHP.Text;
String URL = "http://api.clickatell.com/http/sendmsg?user=myuser&password=mypassword&api_id=myapi_id&to=";
String Text = "&text=" + txtSMS.Text;
btnSMS.PostBackUrl = URL + HP + Text;
string username;
//Sql Connection to access the database
SqlConnection conn6 = new SqlConnection("MY CONNECTION");
//Opening the Connnection
conn6.Open();
string mySQL;
username = HttpContext.Current.User.Identity.Name;
//Insert the information into the database table Login
mySQL = "INSERT INTO Table_Message(Username, Message, Title, SendTo) VALUES('" + username + "','" + txtSMS.Text.Trim() + "','" + txtTitle.Text.Trim() + "','" + txtHP.Text.Trim() + "')";
SqlCommand cmdAdd = new SqlCommand(mySQL, conn6);
//Execute the sql command
cmdAdd.ExecuteNonQuery();
//Close the Connection
}
Your button should closely reflect as below:
<asp:Button runat="server" ID="btnSMS" UseSubmitBehavior="false" OnClick="btnSMS_Click1" Text="Send/>
Then in the code-behind build your postback url and then after the submition add the following:
protected void btnSMS_Click1(object sender, EventArgs e)
{
String HP = txtHP.Text;
String URL = "http://api.clickatell.com/http/sendmsg?user=myuser&password=mypassword&api_id=myapi_id&to=";
String Text = "&text=" + txtSMS.Text;
string UrlFinal = URL + HP + Text;
string username;
//Sql Connection to access the database
SqlConnection conn6 = new SqlConnection("Data Source=19-17\\sqlexpress;" + "Initial Catalog = Suite2; Integrated Security =SSPI");
//Opening the Connnection
conn6.Open();
string mySQL;
username = HttpContext.Current.User.Identity.Name;
//Insert the information into the database table Login
mySQL = "INSERT INTO Table_Message(Username, Message, Title, Startdate, Enddate, SendTo) VALUES('" + username + "','" + txtSMS.Text.Trim() + "','" + txtTitle.Text.Trim() + "','" + txtHP.Text.Trim() + "')";
SqlCommand cmdAdd = new SqlCommand(mySQL, conn6);
//Execute the sql command
cmdAdd.ExecuteNonQuery();
//Close the Connection
this.ClientScript.RegisterStartupScript(this.GetType(),
"navigate",
"window.open('" + UrlFinal + "');",
true);
}
This should open in a new windows. Alternatively one can use window.navigate as well which will open another page in the same browser.
I think a better way to implement this is to make the form post to a URL in your site, which you can react to, then do the post to the service you're using from your server, and redirect the user to the appropriate URL.
UPDATE
I assume you're using web-forms here.
Issue is that you can post to the URL you want, but your user doesn't see any change on the page. Another issue is that, you're putting your password and API_ID for the service in the post back URL, which is visible to anyone who can navigate to your page and hit F12 key. Making your API_ID available to public is a big mistake. It supposed to be something only you and the remote service should know.
Here's my solution.
When you first load the page, you'll show an empty form and allow the user to enter the data you want.
Then in the click event Handler, you get the data you want and post to the service manually.
A HTTP post contains a list of key-value pairs that you want to send to the service. You have to build the post request manually, and get the response from the remote service.
So here's how your click handler should look like.
protected void btnSMS_Click1(object sender, EventArgs e)
{
Dictionary<string,string> postValues = new Dictionary<string,string>();
// gather the key value pairs ou want from your controls and add them to the dictionary.
// and call postSynchronous method (which I'll explain below)
string result = postSynchronous(URLtoPOST,postValues);
if (result= yourSuccessValue){
// redirect to a page that says.. 'Yay!! you successfully posted to the service ?'
Server.Transfer("successPage.aspx");
}else
{
// what to do if it fails ?
}
}
Now the postSynchronous method (or what ever you want to call it) is something you have to write manually, which will take a RUL to post to and a list of key-value pairs to send with the post, then build the actual post request.
Have a look at this link for a tutorial to learn how to do this.
http://petewarden.typepad.com/searchbrowser/2008/09/how-to-post-an.html
I tried to embed the code for the method and any related methods but it was too long.
So, what's better when doing things this way is that you never send your API keys or passwords down to your user's browser, so they'll never know it. (Otherwise they can use the keys to access the service as you which is a security hole)
What's bad in this, compared your solution is that you're posting on your server, which brings that CPU and network load to your server. But I guess it's good trade off for the security benefits you get.
hope this helps. and feel free to ask any questions.
Is it possible to add the property target="_blank" to the btnSMS item?
If it's a link element it would look like this:
id="btnSMS" href="" target="_blank"
An important tangent --- you are leaving yourself dangerously open to a SQL injection attack.
You should never, never, never concatenate text input from a user with a SQL query.
Use #Parameter values in your SQL query string and then use the SqlCommand parameters to replace those values.
string mySQL = "INSERT INTO Table_Message(Username, Message, Title, SendTo) VALUES(#Username, #Message, #Title, #SendTo)";
SqlCommand cmdAdd = new SqlCommand(mySQL, conn6);
cmdAdd.Parameters.AddWithValue("#Username", username);
cmdAdd.Parameters.AddWithValue("#Message", txtSMS.Text.Trim());
cmdAdd.Parameters.AddWithValue("#Title", txtTitle.Text.Trim());
cmdAdd.Parameters.AddWithValue("#SendTo", txtHP.Text.Trim());
cmdAdd.ExecuteNonQuery();
It would be even better to specify the type explicitly:
cmdAdd.Parameters.Add("#Username", SqlDbType.NVarChar, 100).Value = username;
See Troy Hunt's awesome series - http://www.troyhunt.com/2010/05/owasp-top-10-for-net-developers-part-1.html

Categories

Resources