I am just starting work with ASP.NET C# and my database is SQL Server. I am trying to write a query where I want to use with prepared statement.
This is a query that allowing log in to user:
SqlParameter UserName = new SqlParameter("#user", SqlDbType.NVarChar, 30);
SqlParameter Password = new SqlParameter("#pass", SqlDbType.NVarChar, 20);
UserName.Value = user.ToLower();
Password.Value = pass;
SqlCommand command = new SqlCommand(null, conn);
command.Parameters.Add(UserName);
command.Parameters.Add(Password);
command.CommandText = "SELECT * FROM table_users WHERE user_name = '#user' AND password = '#pass';";
command.Prepare();
SqlDataReader reader = command.ExecuteReader();
bool tmp = reader.HasRows;
tmp variable value always FALSE, even when I enter exist user with correct password.
If i just remove parameters and write the query this way:
command.CommandText = "SELECT * FROM table_users WHERE user_name = '"+user+"' AND password = '"+ pass+"';";
tmp variable get value TRUE for exists users.
I tried to use this syntax for INSERT INTO queries and it works correctly.
I already read all the suggestions about changing # to ? and it doesn't work.
I had an error:
Incorrect syntax near '?'. Statement(s) could not be prepared.
Help me please,
Thanks!
You are looking for the literals '#user' and '#pass', rather than the value from the parameter; use:
command.CommandText =
"SELECT * FROM table_users WHERE user_name = #user AND password = #pass;";
instead. Then look into "salted hashes", and why you should never actually store passwords.
BTW, calling Prepare() here isn't helping here. I'm also going to plug dapper-dot-net (free/OSS), which would make this entire thing just:
bool authenticated = conn.Query(
#"select 1 from table_users where user_name = #user and password = #pass",
new {user = user.ToLower(), pass} ).Any();
or, if you want the record:
var tableUser = conn.Query<TableUser>(
#"select * from table_users where user_name = #user and password = #pass",
new {user = user.ToLower(), pass} ).SingleOrDefault();
Related
The following subroutine should update the text in the database to new user input, however, when I check the database nothing has changed?
private void UpdatePassword(string Username, string NewPassword)
{
string SQLStatement =
"UPDATE [Login Details] SET [PASSWORD] = #password WHERE [USERNAME] = #username";
OleDbConnection connString = new OleDbConnection(#"Provider = Microsoft.ACE.OLEDB.12.0; Data Source = TrigonometryToolDatabase.accdb");
OleDbCommand cmd = new OleDbCommand(SQLStatement, connString);
cmd.Parameters.AddWithValue("#username", Username);
cmd.Parameters.AddWithValue("#password", NewPassword);
connString.Open();
cmd.ExecuteNonQuery();
connString.Close();
}
Parameters in MS Access / OleDB is positional - they are NOT matched on name (like in ADO.NET / SQL Server) - but on their position in the query string.
You have
string SQLStatement =
"UPDATE [Login Details] SET [PASSWORD] = #password WHERE [USERNAME] = #username";
so the first parameter you pass in will need to be the password, the username comes second.
However, in your code, you're doing it just the other way around:
cmd.Parameters.AddWithValue("#username", Username);
cmd.Parameters.AddWithValue("#password", NewPassword);
So flip these two lines, and you should be good:
cmd.Parameters.AddWithValue("#password", NewPassword);
cmd.Parameters.AddWithValue("#username", Username);
I'm trying to login to my application from my microsoft access database I'm making right now but I'm getting this error "Syntax error (comma) in query expression ('StudentID', 'Password')." Can anyone give me a fix please?
This is the code where the error is coming from:
con.Open();
string login = "SELECT (StudentID, Password) FROM Student WHERE StudentID = '"+txtStudentID+ "' and Password = '" + txtPassword + "'";
cmd = new OleDbCommand(login, con);
OleDbDataReader dr = cmd.ExecuteReader();
Take off the parentheses:
SELECT StudentID, Password FROM Student ...
And while using parameters to prevent SQL injection is very important (even if this is a school or learning project, as bad habits are hard to break), it is not the source of the problem.
You should remove the parentheses first, also using string interpolation is much better and more readable!
con.Open();
string login = $"SELECT StudentID, Password FROM Student WHERE StudentID = '{txtStudentID.text}' and Password = '{txtPassword.text}' ";
cmd = new OleDbCommand(login, con);
OleDbDataReader dr = cmd.ExecuteReader();
But the complete solution is the below and for avoiding SQL Injection you should use SQL parameters :
SqlParameter userName = new SqlParameter()
{
ParameterName = "#UserName",
DbType = DbType.String,
Direction = ParameterDirection.Input,
Value = txtStudentID.text
};
SqlParameter password = new SqlParameter()
{
ParameterName = "#Password",
DbType = DbType.String,
Direction = ParameterDirection.Input,
Value = txtPassword.text
};
SqlCommand command = new SqlCommand
{
Connection = connection,
CommandType = CommandType.Text,
CommandText = $"SELECT * FROM Student WHERE StudentID = #UserName and Password = #Password"
};
command.Parameters.Add(userName);
command.Parameters.Add(password);
con.Open();
....
I have a code that should test a login.
When I execute literally, it works, returning one row (that's expected). When I use parameters on sqlcommand, I don't get any row.
It works (literal values for username and password):
string strConn = 'string connection';
SqlConnection conn = new SqlConnection(strConn);
SqlCommand sqlCommand = new SqlCommand();
sqlCommand.Connection = conn;
sqlCommand.Parameters.Clear();
sqlCommand.CommandText = #"select *
from
Usuario
where
Username = 'test' and
Password = CONVERT(VARCHAR(32), ashBytes('MD5', 'test'), 2)";
conn.Open();
SqlDataReader ret = sqlCommand.ExecuteReader();
But it doesn't work (parameters values for username and password):
string strConn = 'string connection';
SqlConnection conn = new SqlConnection(strConn);
SqlCommand sqlCommand = new SqlCommand();
sqlCommand.Connection = conn;
sqlCommand.Parameters.Clear();
sqlCommand.CommandText = #"select *
from
Usuario
where
Username = #login and
Password = CONVERT(VARCHAR(32), ashBytes('MD5', #pass), 2)";
SqlParameter user = new SqlParameter("#login", SqlDbType.NVarChar, 50) { Value = "test" };
SqlParameter pass = new SqlParameter("#pass", SqlDbType.NVarChar, 50) { Value = "test" };
List<SqlParameter> list = new List<SqlParameter>();
list.Add(user);
list.Add(pass);
sqlCommand.Parameters.AddRange(list.ToArray<SqlParameter>());
conn.Open();
SqlDataReader ret = sqlCommand.ExecuteReader();
I don't have an sintax error or something like that. The second code just don't returns rows.
I've tried to use sqlCommand.Parameters.AddWithValue, but I have no success too.
'test' and N'test' are not the same thing when you convert them to a hash. One is ASCII and the other is Unicode. If they are both ASCII then use SqlDbType.VarChar (not SqlDbType.NVarChar) in your parameter.
Difference illustrated in Sql
DECLARE #passUnicode Nvarchar(100) = N'test'
DECLARE #passAscii varchar(100) = 'test'
SELECT CONVERT(VARCHAR(32), HashBytes('MD5', #passAscii), 2) AS [Md5OfAscii]
, CONVERT(VARCHAR(32), HashBytes('MD5', #passUnicode), 2) AS [Md5OfUnicode]
Results
098F6BCD4621D373CADE4E832627B4F6, C8059E2EC7419F590E79D7F1B774BFE6
Side notes
Password Hashing
I recommend against storing passwords as MD5, MD5 is simply not secure. There are plenty of alternatives out there like pbkdf2, bcrypt, and scrypt to name a few of the more generally accepted secure password hashing algorithms.
c# structure
When working with Ado.net (or with any resources for that matter) you should wrap your Connections, DataReaders, Adapters, etc that implement IDisposable in using blocks. This will ensure external resources are always freed, even in the event of a connection.
string connectionString = "";
using (var connection = new SqlConnection(connectionString))
{
using (var command = new SqlCommand
{
CommandText = #"select * from Usuario where Username = #login and Password = CONVERT(VARCHAR(32), HASHBYTES('MD5', #pass), 2)",
CommandType = CommandType.Text,
Connection = connection
})
{
command.Parameters.Add(new SqlParameter("login", SqlDbType.VarChar, 50) { Value = "test" });
command.Parameters.Add(new SqlParameter("pass", SqlDbType.VarChar, 50) { Value = "test" });
connection.Open();
using (var dataReader = command.ExecuteReader())
{
// do some stuff
}
}
}
I am trying to get a simple SQLite database working. I'm using the official SQLite extension for C# and I'm using DataGrip from IntelliJ to verify the data is there, yet my C# program doesn't get any results.
This is the code that executes the query:
SQLiteConnection connection = new SQLiteConnection(DbDsn);
User user = new User();
using (connection)
{
connection.Open();
string sql = "SELECT * FROM user WHERE username = #username ;";
SQLiteCommand command = new SQLiteCommand(sql, connection);
command.Prepare();
command.Parameters.AddWithValue("#username", username);
SQLiteDataReader reader = command.ExecuteReader();
if (reader.Read())
{
user.Id = (int) reader["id"];
user.Username = reader["username"] as string;
user.Password = reader["password"] as string;
user.Name = reader["name"] as string;
user.LastName = reader["last_name"] as string;
user.Type = (UserTypes) reader["type"];
}
else
{
throw new ObjectNotFoundException();
}
connection.Close();
}
And this is the result of a simple Select * From user; query on the user table (done on DataGrip):
id username passw… name last_name type
1 managertest oAWpW… BENJAMIN ARIEL NAVA MARTINEZ 1
2 clerktest iRYMz… EMPLEADO PRUEBA 0
As you can see, the records are there (an I've verified that the query is being performed on the exact same file), however, the C# program seems to skip the if statement (because read returns false) as if there were no rows in the database, what is the problem here?
Call SQLiteCommand.Prepare AFTER you have completed constructing your command
//...
string sql = "SELECT * FROM user WHERE username = #username ;";
SQLiteCommand command = new SQLiteCommand(sql, connection);
command.Parameters.AddWithValue("#username", username);
// Call Prepare after setting the Commandtext and Parameters.
command.Prepare();
SQLiteDataReader reader = command.ExecuteReader();
//...
I'm using a query to update a column value on table and also retrieve the updated value using the following way on the ASP.NET site. Is there any way to use single query instead of double queries as below?
using (SqlConnection connection = new SqlConnection(connStr)){
string updateUserQuery = "Update user_master set login_failed_attempts = login_failed_attempts + 1 where id = #id; Select #login_failed = login_failed_attempts from user_master where id = #id";
SqlCommand cmd = new SqlCommand(updateUserQuery, connection);
cmd.Parameters.Add("#id", SqlDbType.Int).Value = user_id;
SqlParameter outputIdParam = new SqlParameter("#login_failed", SqlDbType.Int)
{
Direction = ParameterDirection.Output
};
cmd.Parameters.Add(outputIdParam);
cmd.ExecuteNonQuery();
int loginFailedAttempts = int.Parse(outputIdParam.Value.ToString());
}
Updated code is given below after Matthew's answer.
string updateUserQuery = "Update user_master set login_failed_attempts = login_failed_attempts + 1 OUTPUT INSERTED.login_failed_attempts where id = #id";
SqlCommand cmd = new SqlCommand(updateUserQuery, connection);
cmd.Parameters.Add("#id", SqlDbType.Int).Value = user_id;
int loginFailedAttempts = (int)cmd.ExecuteScalar();
Use an OUTPUT clause.
UPDATE user_master
SET login_failed_attempts = login_failed_attempts + 1
OUTPUT INSERTED.login_failed_attempts
WHERE id = #id
Then change your ExecuteNonQuery to an ExecuteScalar and use the result from that accordingly.
You could also change it to an ExecuteReader and pull back multiple records, but given your use of #id I'm assuming you don't want that.