Updating MySql counter when users log in - c#

I have a website where users can log in. The client wants there to be a way to record the number of times any particular user logs in. I have a "Counter" row in the table. How do I program the app (built in C# ASP.NET) to update the counter when people log in? Is this code correct:
cmd.ExecuteNonQuery = "UPDATE Counter FROM brokercenter"
I just recently graduated (as in the 10th of this month) so I am new to this, plus I know nothing about databases, I am just learning on the job. Please let me know if I need any other parameter or connection string or aything else? This is in the button click event and there is already a connection string there to check the username and password so I don't think I need another connection string, but I don;t know for sure. Thanks in advance!
For that matter, here is the whole event (the login stuff works fine, just the update is my question):
string connectionString =
ConfigurationManager.ConnectionStrings["moverschoiceConnectionString"].ConnectionString;
OdbcConnection conn = new OdbcConnection(connectionString);
conn.Open(); OdbcCommand cmd = new OdbcCommand();
cmd.Connection = conn;
cmd.CommandText = "select Email, Password from brokercenter where Email = '" + txtLoginEmail.Text + "'";
OdbcDataReader reader = cmd.ExecuteReader();
while(reader.Read())
{
if (reader["Password"].ToString() == txtLoginPassword.Text)
{
reader.Close();
if (cbRememberMe.Checked == true)
{
Response.Cookies["username"].Value = txtLoginEmail.Text;
Response.Cookies["username"].Expires = DateTime.Now.AddMonths(1);
Response.Cookies["password"].Value = txtLoginPassword.Text;
Response.Cookies["password"].Expires = DateTime.Now.AddMonths(1);
}
else
{
Response.Cookies["username"].Expires = DateTime.Now.AddMonths(-1);
Response.Cookies["password"].Expires = DateTime.Now.AddMonths(-1);
}
Response.Redirect("BrokerResources.aspx");
}
else
{
lblLoginError.Text = "Invalid Password";
}
}
lblLoginError.Text = "Invalid Email or Password";
reader.Close();
cmd.ExecuteNonQuery = "UPDATE counter FROM brokercenter";
}

For a start, you should read about using UPDATE in MySQL's Reference Manual.
There is even an example for exactly what you want to do.
Quote from the link:
If you access a column from the table to be updated in an expression,
UPDATE uses the current value of the column. For example, the
following statement sets col1 to one more than its current value:
UPDATE t1 SET col1 = col1 + 1;
This is basically all you need, you just need to add a WHERE clause and filter for the username or for the email.
Plus, you should read about SQL Injection because of this:
where Email = '" + txtLoginEmail.Text + "'";
Concatenating strings like this to pass parameters can cause problems as described in the link.
Here's an example how to do it better.

cmd.ExecuteNonQuery = String.Format("UPDATE brokercenter SET counter = {0} WHERE Email = {1}", myCounter++, txtLoginEmail.Text);
where "myCounter" is a local counter variable (which should also be read from the database). Does this make sense now?

Related

How to prevent SQL Injection in this code?

How can i prevent these code of getting SQL injected? It's a login system that i'm learning. Here's the code!
if (!(string.IsNullOrWhiteSpace(textBox1.Text)) && !(string.IsNullOrWhiteSpace(textBox2.Text)))
{
MySqlConnection mcon = new MySqlConnection("datasource = 127.0.0.1; port = 3306; username = root; password = ; database = rpgmaster;");
mcon.Open();
DataTable table = new DataTable();
MySqlDataAdapter adapter = new MySqlDataAdapter("Select * From users where Username = '" + textBox2.Text + "' and password = '" + textBox1.Text + "'", mcon);
adapter.Fill(table);
if (table.Rows.Count <= 0)
{
MessageBox.Show("Você não está registrado!");
}
else
{
MessageBox.Show("Logado com sucesso! ");
}
mcon.Close();
}
Thanks for the help! Really appreciate it!
If you're learning, you could perhaps move on from this old low level way of doing data access and use something a bit more modern and easy. Dapper is an example of a library that isn't a huge leap above what you already know but makes your life a lot nicer:
using(var conn = new MySqlConnection("conn str here"){
var sql = "SELECT count(*) FROM tblUsers WHERE username = #u AND password = #p";
var prm = new {
u = txtUsername.Text, //give your textboxes better names than textbox2,textbox1!
p = txtPassword.Text.GetHashCode() //do NOT store plain text passwords!
};
bool valid = await conn.QuerySingleAsync<int>(sql, prm) > 0;
if(valid)
... valid login code
else
... invalid login
}
Some notes on this:
dapper is a device that you simply give your sql and parameter values to
the sql holds #parameters names like #u
an anonymous typed object has properties called the same name as the parameter name, with a value, like u = "my username"
use async/await when running queries; dapper makes this easy. Avoid jamming your UI up on queries that take 10 seconds to run
in this case you only need to ask the db to count the matching records, you don't need to download them all to find out if there are any, so we use QuerySingleAsync<int> which queries a single value of type it, and if it's more than 0, the login was valid
never store password in a database in plaintext. Use a one way hashing function like MD5, SHA256 etc, even the lowly string.GetHashCode is better than storing plaintext, particularly because people use the same passwords all the time so anyone breaking into your db (very easy; the password is in the code) will reveal passwords treat people probably use in their banking etc. We can't really be asking, on the one hand, how to prevent a huge security hole like SQL injection, and then on the other hand leave a huge security hole like plaintext passwords ;)
always name your textboxes a better name than the default textboxX - it takes seconds and makes your code understandable. If Microsoft called all their class property names like that, then the entire framework would be full of things like myString.Int1 rather than myString.Length and it would be completely unusable
life is too short to spend it writing AddWithValue statements; use Dapper, Entity Framework, strongly typed datasets.. Some db management technology that eases the burden of writing that code
Where Dapper makes things really nice for you is its ability to turn objects into queries and vice versa; this above is just a basic count example, but suppose you had a User class:
class User
{
string Name { get; set; }
string HashedPassword { get; set; }
int age {get; set; }
}
And you had a table tblUsers that was similar (column names the same as the property names), then you could query like:
User u = new User() { Name = "someuser" };
User t = await conn.QuerySingleAsync<User>("SELECT Name, HashedPassword, Age FROM tblUsers WHERE Name = #Name", u);
We want to look up all the info of the someuser user, so we make a new User with that Name set (we could also use anonymous type, like the previous example) and nothing else, and we pass that as the parameters argument. Dapper will see the query contains #Name, pull the contents of the Name from the u user that we passed in, and run the query. When the results return it will create a User instance for us, fully populated with all the data from the query
To do this old way we'd have to:
have a command,
have a connection,
add parameters and values,
open the connection,
run the sql,
get a reader,
check if the reader had rows,
loop over the reader pulling the first row,
make a new User,
use reader.GetInt/GetString etc to pull the column values out one by one and
finally return the new user
oh and dispose of all the db stuff, close the connection etc
Writing that code is repetitive, and it is really boring. In computing, when we have something repetitive and boring, that we need to do thousands of times through out life (like serializing to json, calling a webservice, designing a windows UI) we find some way to make the computer do the repetitive boring bit; they do it faster and more accurately than we can. This is exactly what Dapper does; it does away with that boring repetitive and reduces it to a single line where you say what you want back, using what query, with what parameters. And it keeps your UI working:
await x.QueryAsync<type>(query, parameters)
Win. Seek out some Dapper tutorials! (I have no affiliation)
Try using parameters please see updated sample of your code below:
if (!(string.IsNullOrWhiteSpace(textBox1.Text)) && !(string.IsNullOrWhiteSpace(textBox2.Text)))
{
using (MySqlConnection mcon = new MySqlConnection("datasource = 127.0.0.1; port = 3306; username = root; password = ; database = rpgmaster;"))
{
mcon.Open();
MySqlCommand cmd = new MySqlCommand("Select * from users where username=?username and password=?password", mcon);
cmd.Parameters.Add(new MySqlParameter("username", textBox2.Text));
cmd.Parameters.Add(new MySqlParameter("password", textBox1.Text));
MySqlDataReader dr = cmd.ExecuteReader();
if (dr.HasRows == true)
{
MessageBox.Show("Você não está registrado!");
}
else
{
MessageBox.Show("Logado com sucesso! ");
}
}
}
Use parameters to pass and check their length, Use stored procedure instead of a query in the code. Use columns instead of * in Select. And please make sure you don't store the plain password in the DB
Use Parameters
using (MySqlConnection mcon = new MySqlConnection(connectionString))
{
string commandText = "SELECT * FROM users WHERE Username = '#tbxText'"
SqlCommand command = new SqlCommand(commandText, mcon);
command.Parameters.AddWithValue("#tbxText", textBox2.Text);
}

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.

Access Database c# query Problems

Here is the background of my problem: I have a combobox that when users start typing, it should retrieve suggested items from a column in database table. The user starts inputing name and the program should suggest names by looking at both first and last names (database has separate tables for both )
Here is the code that I had:
try{
String temp = nameCBox.Text;
AutoCompleteStringCollection namesSuggestion = new AutoCompleteStringCollection();
OleDbConnection conn = new OleDbConnection("Provider=Microsoft.ACE.Oledb.12.0;Data Source=C:\\LogEntry\\LogEntry.accdb; Persist Security Info = False;");
OleDbDataReader reader;
conn.Open();
String text2send = "Select Name from [Teachers] where FName like '" + temp + "' OR LName like '" + temp + "' Group by [Name]";
OleDbCommand cmd = new OleDbCommand(text2send, conn);
reader = cmd.ExecuteReader();
if (reader.HasRows == true)
{
while (reader.Read())
namesSuggestion.Add(reader["temp"].ToString());
}
reader.Close();
nameCBox.AutoCompleteCustomSource = namesSuggestion;
conn.Close();
}
errors:
1) I see no suggestions in the combo box
2) When I type in the combo box, it highlights the text and when I type something else again, it will write on the previous typed character.
Please Help
desktopmaker
Erm
What's this doing
namesSuggestion.Add(reader["temp"].ToString());
reader is returning a column called Name..
I wouldunless the user is typing it in expect the search string to contain a wild card.
E.g. Like 'John%', or Like '%Smith' in standard sql, access uses * instead of % I seem to remember.
Oh and presumably you aren't worried about sql injection attacks??
Since you are using Like operator, try to make the input between look like this : %input%
To get something like :
Take a look at this, it may help
var sql = String.Format("Select Name from [Teachers] WHERE FName Like '%{0}%' OR LName Like '%{0}%'", temp);
Other points may be helpful about your current code :
Use the using statement in your code, it dispose the resources, and for the Connection, it close it.
using(var conn = new OleDbConnection("ConnectionStrong"))
{
//code
}
The best, is to use a parameterized query Link

Exception while inserting values in to SQL table through C#

HI i had manullay created textbox's and then used it for creating a new user. I am using SQL SERVER 2005 for backend and Visual Server 2008 for front..
I have this LoginAccount table which stores details of the new user created. When i Click the button(in which i have written code to create a new user through SQL insert),
string strConnection = ConfigurationManager.ConnectionStrings"FHDLConnectionString"].ToString();
SqlConnection sqlConnection = new SqlConnection(strConnection);
string username = TextBox1.Text;
string password = TextBox2.Text;
string confirmpass = TextBox3.Text;
string SQLQuery = "Select username From LoginAccount where '" + username + "'";
string SQLQuery1 = "Insert into LoginAccount values ('" + username + "','" + password + "')";
SqlCommand command = new SqlCommand(SQLQuery, sqlConnection);
SqlCommand command1 = new SqlCommand(SQLQuery1, sqlConnection);
sqlConnection.Open();
string CheckUsername = "";
if (password.ToString() != confirmpass.ToString())
{
Literal1.Text = " Password's does not match ";
}
else
{
try
{
CheckUsername = command.ExecuteScalar().ToString();
}
catch (Exception er)
{
Literal1.Text = " Username already exists ";
}
string insertQuery = "";
try
{
insertQuery = command1.ExecuteScalar().ToString();
Server.Transfer("Login_Created.aspx");
}
catch (Exception er)
{
Literal1.Text = " Could not create the user, Please retry with some other option ";
}
}
sqlConnection.Close();
I am getting these exception's
An expression of non-boolean type specified in a context where a condition is expected, near 'fhdl'
This error i got at the first catch
and
Object reference not set to an instance of an object.
This for at the last catch.
But the main thing is i am able to insert the username and password into the LoginAccount table!!!!!!! i.e. when i saw the table contents i could see the new user created in that table.
The other thing is This code executed perfectly once before but not now :(
Please could anyone tell me where am i going wrong?
I am new to C# with SQl ...
1 - ExecuteScalar() means that there's a return value. The insert doesn't have a return value. Use ExecuteNonQuery instead.
2 - Also, for the insert statement, specify the fields you're inserting into. Without specifying the fields, it's trying to insert into the first two columns of the table, which may not be username and password.
insert into LoginAccount(UserName, Password) values ....
3 - your select statement is incorrect.
select from LoginAccount where UserName='... You're missing the field name.
Three things:
Your first query doesn't do what you want it to. It resolves to:
Select username From LoginAccount where 'username'
You want to check the username against the database.
This code leaves you wide open to SQL Injection attacks. See this article for how to prevent them in C#.
On a similar note, you really don't want to store passwords in the clear in your database either.

Categories

Resources