I have designed a Log In System using C# where the username and password is checked in SQL server 2008 before loading the main page. I wish to encrypt the stored password on the database. Is it possible to do it using C# and SHA1 algorithm?
Following is my stored procedure:
ALTER procedure [dbo].[proc_UserLogin]
#userid varchar(20),
#password nvarchar(50)
As
declare
#ReturnVal varchar(500)
SET NOCOUNT ON
if exists(select userid,password from LoginManager where userid=#userid and password=#password)
set #ReturnVal='0|Logged in Successfully'
else
set #ReturnVal='1|Login Failed/Username does not exist'
select #ReturnVal
C# Code
public void button1_Click(object sender, EventArgs e)
{
mainform = new Form1();
string[] v;
OleDbConnection conn = new OleDbConnection("File Name=E:\\Vivek\\License Manager\\License Manager\\login.udl");
try
{
conn.Open();
string query = "EXEC dbo.proc_UserLogin'" + username.Text+ "', '" + password.Text+"'";
OleDbCommand cmd = new OleDbCommand(query, conn);
string s = Convert.ToString(cmd.ExecuteScalar());
v= s.Split('|');
if (v[0]=="0")
{
mainform.Show();
this.Hide();
}
else
{
MessageBox.Show("Please enter correct user credentials and try again");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
conn.Close();
}
I have gone through similar questions asked by other users here, but they were not working for me. Can anyone suggest changes to the code, so that password encryption can be accomplished?
Thanks
Hash and salt passwords in C#
https://crackstation.net/hashing-security.htm
https://www.bentasker.co.uk/blog/security/201-why-you-should-be-asking-how-your-passwords-are-stored
As I stated in my comments, hashing passwords is something that you probably shouldn't be doing yourself.
A few things to note:
SHA1 is not recommended for passwords
Passwords should be salted
You should use a verified userstore framework rather than attempting to create your own, as you will likely "do it wrong"
I'm sure there are many more
That being said, to accomplish your specific question, you would want something like this:
Users
----
userId
passwordHashed
passwordHashed stores a hashed version of the user's password (the plain text password is never stored anywhere in persistence.)
for checking for valid password something like this is done:
ALTER procedure [dbo].[proc_UserLogin]
#userid varchar(20),
#password nvarchar(50)
As
declare
#ReturnVal varchar(500)
SET NOCOUNT ON
if exists(select userid,password from LoginManager where userid=#userid and password=HASHBYTES('SHA1', #password))
set #ReturnVal='0|Logged in Successfully'
else
set #ReturnVal='1|Login Failed/Username does not exist'
select #ReturnVal
For inserting/updating user passwords, you need to make sure to store the hashed password not the plain text password, as such;
INSERT INTO users(userId, passwordHashed)
VALUES (#userId, HASHBYTES('SHA1', #rawPassword)
or
UPDATE users
SET passwordHased = HASHBYTES('SHA1', #rawPassword)
WHERE userId = #userId
EDIT:
just realized you're asking how to accomplish the hash in C#, not SQL. You could perform the following (taken from Hashing with SHA1 Algorithm in C#):
public string Hash(byte [] temp)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
var hash = sha1.ComputeHash(temp);
return Convert.ToBase64String(hash);
}
}
Your code snip could be:
conn.Open();
string query = "EXEC dbo.proc_UserLogin'" + username.Text+ "', '" + this.Hash(System.Text.Encoding.UTF8.GetBytes(password.Text))+"'";
OleDbCommand cmd = new OleDbCommand(query, conn);
You should also note that you should parameterize your parameters to your stored procedure rather than passing them in the manner you are - which it looks like you already have a separate question in regarding that.
Related
This question already has answers here:
SQL Case Sensitive String Compare
(6 answers)
Closed 5 years ago.
I'm new to coding in asp.net . I have done a (web form) login page and its validation in asp.net + c#. The problem is when i enter the password corresponding to the email in lower-case/upper-case letters, it grants me entry which is not supposed to happen. I have gone through a number of codes posted here for login page but the same thing is happening.
For eg,if the password in db is "ss"and if we type "ss","Ss", "sS" or "SS" ; i'm able to sign in. How can i stop this from happening?
I'm posting my code here. Please help.
protected void btn_login1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(#"Data Source=SHA\SQLE2012;Initial Catalog=OnlineShoppingStore;User ID=sa;Password=56238");
con.Open();
SqlCommand cmd = new SqlCommand("usp_ViewUserByUserId", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Email", txt_email.Text);
cmd.Parameters.AddWithValue("#Password", txt_password.Text);
string output = cmd.ExecuteScalar().ToString();
cmd.ExecuteNonQuery();
if (output =="1")
{
Response.Write("<script>alert('Login Successful!!')</script>");
Session["Email"] = txt_email.Text;
Response.Redirect("Home.aspx");
}
else
Response.Write("<script>alert('Login Failed! Incorrect username/password')</script>");
con.Close();
}
This is my stored Procedure
ALTER PROCEDURE [dbo].[usp_ViewUserByUserId]
#Email as varchar(50),
#Password as varchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
select count(*) from tbl_Customer where Email = #Email and Password = #Password
end
Thank you all for taking the time to give suggestions.
The code below did the trick
select count(*)
from tbl_Customer
where Email = #Email and BINARY_CHECKSUM(Password) = BINARY_CHECKSUM(#Password)
I'm new to programming and I'm programming with Visual Studio. I'm trying to get an input from user (a name of a site like google.com) and search for the site name toward my tables (I have different domain tables such as .com , .org , etc).
So, I'm trying to write this stored procedure which selects from a table without actual table name (I'm trying to pass table name from input to stored procedure) here is my stored procedure:
CREATE PROCEDURE test
#link nvarchar(50)
AS
SELECT *
FROM #link
I have defined linksg (set and get) like this:
public string linksg
{
get { return link; }
set { link = value; }
}
and this is how I defined linksg in a function (my search_sitedomain function takes a domain like .com and gives a link to a table which includes your sitename like .com table):
public void search_sitedomain()
{
SqlConnection con = new SqlConnection("Data Source=SM;Initial Catalog=mohandesi-net;Integrated Security=True;Pooling=False");
SqlCommand cmd = new SqlCommand("search_sitedomain", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#domain", domain);
con.Open();
string link = Convert.ToString(cmd.ExecuteScalar());
linksg = link;
MessageBox.Show(link);
}
The search_sitedomain function works perfectly fine and returns the link but my stored procedure doesn't work like it can't replace #link with a tablename (like .com)
So what am I doing wrong?
In my opinion having a parametrized table name is a huge security hole, but it's possible to do it as follows:
CREATE PROCEDURE test
#link nvarchar(50)
AS
EXEC ('SELECT * FROM dbo.['+#link+']');
I am trying to execute a query with a condition like if username already present then update the row, else insert username and password.
This is my code below:
using (SqlCommand cmd = new SqlCommand("INSERT INTO Users(Username,Password) VALUES(#User,#password) ON DUPLICATE KEY UPDATE Username=VALUES(Username), Password=VALUES(Password)"))
{
cmd.Connection = con;
cmd.Parameters.AddWithValue("#User", TextBox3.Text);
cmd.Parameters.AddWithValue("#password", Pwd);
con.Open();
cmd.ExecuteNonQuery();
}
I got the following error:
Incorrect syntax near the keyword 'ON'.
I am not able to figure out what is wrong in this. Can anyone please help me out?
In SQL Server you need to use a query something like this:
-- check if exists (by username) - if found, update password
IF EXISTS (SELECT * FROM dbo.Users WHERE Username = #User)
UPDATE dbo.Users
SET Password = #password
WHERE Username = #User
ELSE
INSERT INTO dbo.Users(Username, Password)
VALUES(#User, #password)
And as mentioned in my comments - do not use the .AddWithValue function (see linked blog post for details) but use this instead:
cmd.Parameters.Add("#User", SqlDbType.VarChar, 100).Value = TextBox3.Text;
And also, please do not store your passwords in clear text in the database!
It looks like you're using MySQL syntax. I don't think SQL Server has ON DUPLICATE KEY. You'd probably want a MERGE statement.
#marc_s
String query = #"IF EXISTS (SELECT * FROM Users WHERE Username = # User)
UPDATE Users
SET Password = #password
WHERE Username = # User
ELSE
INSERT INTO Users(Username, Password)
VALUES(# User, #password)";
using (SqlCommand cmd = new SqlCommand(query))
I used the code you gave and used to debug points to check if the code is executing ,and it was, still it is not updating or Inserting the values .I cant run the query in SQL server cause each time i open the query window VSudio restarts,i am using trial version of Visual Studio
I am trying to update the password for an existing SQL login using Alter LOGIN
I know the following works
ALTER LOGIN [username1] WITH PASSWORD = 'somenewpassword123';
However when I try to use a local variable
DECLARE #newpass nvarchar(max);
SET #newpass = 'P#ssw0rd12345';
ALTER LOGIN [username1] WITH PASSWORD = #newpass;
This fails. Adding [] braces to the variable seems to resolve this within the SSMS query editor however using this programmaticlly by writing out the query in C# it fails as the above statement with the same error ( syntax error at PASSWORD)
Code within c# app
public static int UpdateSqlLoginPassword(DbContext context, string loginName, string password)
{
try
{
string updatePassword =
#" SET NOCOUNT ON
DECLARE #loginName AS nvarchar(max) = {0}
DECLARE #password AS nvarchar(max) = {1}
EXEC('
USE master
ALTER LOGIN ['+ #loginName + '] WITH PASSWORD = ['+ #password + ']
')";
return context.Database.ExecuteSqlCommand(updatePassword, loginName, password);
}
catch (Exception)
{
return -2;
}
}
I have also tried to hash the password (thinking that was the issue with the variable) but the syntax here is not being accepted
DECLARE #newpass nvarchar(max);
SET #newpass = 'P#ssw0rd12345';
DECLARE #hashedpass varbinary(max);
SET #hashedpass = HASHBYTES('SHA1', CONVERT(nvarchar(max),#newpass));
ALTER LOGIN [newuser10] WITH PASSWORD = #hashedpass HASHED;
SELECT #hashedpass;
Can anyone help me understand how to update a login's password in sql using a variable instead of a fixed value?
thanks in advance
Update
Based upon a suggestion from Charlie I also tried the following
public static int UpdateSqlLoginPassword(DbContext context, string loginName, string password)
{
try
{
string updatePassword =
#"ALTER LOGIN [' + #loginName +'] WITH PASSWORD = #password ";
return context.Database.ExecuteSqlCommand(updatePassword, new SqlParameter("loginName", loginName), new SqlParameter("password", password));
}
catch (Exception)
{
return -2;
}
}
This still generates a sqlException Incorrect Syntax new '#password'.
If I brace the parameter
public static int UpdateSqlLoginPassword(DbContext context, string loginName, string password)
{
try
{
string updatePassword =
#"ALTER LOGIN [' + #loginName +'] WITH PASSWORD = [' + #password +']";
return context.Database.ExecuteSqlCommand(updatePassword, new SqlParameter("loginName", loginName), new SqlParameter("password", password));
}
catch (Exception)
{
return -2;
}
}
I then generate a sqlException Incorrect syntax near PASSWORD.
Update2
Using the updated suggestions from Charlie I attempted to use the QuoteName function
string sql = #"DECLARE #sql NVARCHAR(500)
SET #sql = 'ALTER LOGIN ' + QuoteName(#loginName) +
' WITH PASSWORD = ' + QuoteName(#password, '''')
EXEC #sql";
return context.Database.ExecuteSqlCommand(sql, new SqlParameter("loginName", loginName), new SqlParameter("password", password));
While it appears that the query string is properly formed the following SQLException is thrown
*The name 'ALTER LOGIN [newuser10] WITH PASSWORD = 't#P#ssw0rd'' is not a valid identifier.
EDIT
After some more reading the error was generated by a syntax error wrapping the #sql allows the query to execute with no errors
string sql = #"DECLARE #sql NVARCHAR(500)
SET #sql = 'ALTER LOGIN ' + QuoteName(#loginName) +
' WITH PASSWORD = ' + QuoteName(#password, '''')
EXEC(#sql)";
On a side note: by simply building the string and running it as
string updatePassword = "USE MASTER ALTER LOGIN [" + loginName + "] WITH PASSWORD = '" + password + "'";
return context.Database.ExecuteSqlCommand(updatePassword);
the above is also a workaround and updates the sql login. While the implementation of this code minimizes the potential for sql injections this is not the most desirable approach.
-Thanks
You need to use parameters at the DbContext level. See this answer for more details, but, here's a code example (adapted from that same page):
string sql = "ALTER LOGIN #loginName WITH PASSWORD = #password";
ctx.Database.ExecuteSqlCommand(
sql,
new SqlParameter("loginName", loginName),
new SqlParameter("password", password));
The purpose of using the parameters here (and everywhere) is to prevent a SQL injection attack. This is especially important given that you are writing code that changes a password.
UPDATE
The ALTER LOGIN statement won't work with variables; it must be done through dynamic SQL. Here's an example of the updated code:
string sql = #"DECLARE #sql NVARCHAR(500)
SET #sql = 'ALTER LOGIN ' + QuoteName(#loginName) +
' WITH PASSWORD= ' + QuoteName(#password, '''')
EXEC #sql ";
ctx.Database.ExecuteSqlCommand(
sql,
new SqlParameter("loginName", loginName),
new SqlParameter("password", password));
Note we're still using the SqlParameters to prevent SQL injection attacks. We are also using the T-SQL method QuoteName to do proper quoting in the SQL we are generating; but this method simply doubles any [ characters (in the first call) or ' characters (in the second). There are many other vectors for a SQL injection attack, so merely relying on QuoteName wouldn't be enough.
I'm using the above answer with Azure SQL and I was getting the "not a valid identifier" error until I surrounded replaced "EXEC #sql" with "EXEC (#sql)". See Msg 203, Level 16, State 2, is not a valid identifier
Additionally, I had to use "ALTER USER" instead of "ALTER LOGIN"
After Preparing SQL query string and executing using c# SQL Command, I was always getting Invalid Identifier error.
It was because QuoteName should get executed before executing change password sql statements.
So I created stored procedure using above solutions then called procedure from c#, it worked for me.
Create procedure usp_updateSqlUsers(#loginName nVarchar(100), #pwd nvarchar(100))
as
begin
DECLARE #sql NVARCHAR(500)
set #sql='Alter LOGIN '+QUOTENAME(#loginName)+' WITH
password=N'+ QUOTENAME(#pwd,'''')
exec sp_sqlexec #sql
end
Then execute from C#
SqlCommand cmd = new SqlCommand("usp_updateSqlUsers", con) {CommandType =
CommandType.StoredProcedure};
var passwordParam = new SqlParameter("#pwd", password);
var sqlLoginParameter = new SqlParameter("#loginName", "SqlLoginName");
cmd.Parameters.Add(passwordParam);
cmd.Parameters.Add(sqlLoginParameter);
cmd.ExecuteNonQuery();
I'm using Visual C# connected to MySQL for study purposes and I'm stuck in throwing an error to the user when he types a username that already exists.
Current code to put things into the database (it may be useless, once my question may be much more about SQL):
s = new sql(); // This calls a class that works as an adapter to connect form with the database
Conn = s.Connection;
Conn.Open();
coma = Conn.CreateCommand();
coma.CommandText = "INSERT INTO test.test (`user`,`password`) VALUES ('"+username.Text+"','"+password.Text+"');";
coma.ExecuteNonQuery();
What I want to do it compare "username.Text" ("username" is a TextBox) with the values on database's "test" table and, if some value match, evoke a MessageBox.Show("Hey guy, this username is already in use! Try something different)
Some points about your code sample
You want to be sure that you dispose of your connection and command objects. For my answer, I've wrapped them in using statements which will take care of that for me.
You do not want to go to the database with unsanitized inputs. I am going to use parameterized queries in the example.
It's not a good idea to store passwords in plain text. I am not going to demonstrate more secure techniques, just know to look for information about encrypting passwords, salt keys, etc.
And now for some code. In this, I'm using OleDb objects, retrofit to your particular database. And, of course, provide appropriate names to tables, columns, etc.
using (OleDbConnection connection = SomeMethodReturningConnection())
using (OleDbCommand command = SomeMethodReturningCommand())
{
command.Parameters.Add(new OleDbParameter("#username", username));
command.CommandText = "Select Count(*) From Users where Username = #username";
connection.Open();
int output = (int)command.ExecuteScalar();
if (output > 0)
{
// username already exists, provide appropriate action
}
else
{
// perform insert
// note: #username parameter already exists, do not need to add again
command.Parameters.Add(new OleDbParameter("#password", password));
command.CommandText = "Insert Into Users (Username, Password) Values (#username, #password)";
command.ExecuteNonQuery();
}
}
Thank you Anthony! Your answer put me on the right track. Although there is something that the people who will read this post should change from your code in order to get it working with Odbc connectors: the way as parameters are parsed and the way as the textbox content is extracted:
using (OdbcConnection connection = SomeMethodReturningConnection())
using (OdbcCommand command = SomeMethodReturningCommand())
{
command.Parameters.Add(new OdbcParameter("#username", username.Text));
command.CommandText = "Select Count(*) From Users where Username = ?";
connection.Open();
int output = (int)command.ExecuteScalar();
if (output > 0)
{
// username already exists, provide appropriate action
}
else
{
// perform insert
// note: #username parameter already exists, do not need to add again
command.Parameters.Add(new OdbcParameter("#password", password.Text));
command.CommandText = "Insert Into Users (Username, Password) Values (?,?)**";
command.ExecuteNonQuery();
}
}
Thank you anyway!