Hi there, Im having some trouble implementing a secure login facility to a website im creating. I have managed to be able to register new members onto the database with hash values.
However i cannot seem to logon using the same credentials. I believe it's a problem with the SQL reader not reading the table correctly. I did origianlly have a SQL Stored Procedure setup.
Code Below (Login.aspx.cs);
Code:
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Security.Cryptography;
using System.Data.SqlClient;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
private static string CreateSalt(int size)
{
// Generate a cryptographic random number using the cryptographic
// service provider
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] buff = new byte[size];
rng.GetBytes(buff);
// Return a Base64 string representation of the random number
return Convert.ToBase64String(buff);
}
private static string CreatePasswordHash(string pwd, string salt)
{
string saltAndPwd = String.Concat(pwd, salt);
string hashedPwd =
FormsAuthentication.HashPasswordForStoringInConfigFile(
saltAndPwd, "SHA1");
hashedPwd = String.Concat(hashedPwd, salt);
return hashedPwd;
}
protected void btnRegister_Click(object sender, EventArgs e)
{
int saltSize = 5;
string salt = CreateSalt(saltSize);
string passwordHash = CreatePasswordHash(txtPassword.Text, salt);
try
{
StoreAccountDetails(txtUserName.Text, passwordHash);
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
}
private void StoreAccountDetails(string userName, string passwordHash)
{
// See "How To Use DPAPI (Machine Store) from ASP.NET" for information
// about securely storing connection strings.
ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["ConnectionString"];
SqlConnection conn = new SqlConnection(settings.ConnectionString);
SqlCommand cmd = new SqlCommand("RegisterUser",conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter sqlParam = null;
//Usage of Sql parameters also helps avoid SQL Injection attacks.
sqlParam = cmd.Parameters.Add("@userName", SqlDbType.VarChar,
20);
sqlParam.Value = userName;
sqlParam = cmd.Parameters.Add("@passwordHash ", SqlDbType.VarChar,
40);
sqlParam.Value = passwordHash;
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch( Exception ex )
{
// Code to check for primary key violation (duplicate account name)
// or other database errors omitted for clarity
throw new Exception("Exception adding account. " + ex.Message);
}
finally
{
conn.Close();
}
}
private bool VerifyPassword(string suppliedUserName, string suppliedPassword)
{
bool passwordMatch = false;
// Get the salt and pwd from the database based on the user name.
// See "How To: Use DPAPI (Machine Store) from ASP.NET," "How To:
// Use DPAPI (User Store) from Enterprise Services," and "How To:
// Create a DPAPI Library" for more information about how to use
// DPAPI to securely store connection strings.
ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings["ConnectionString"];
SqlConnection conn = new SqlConnection(settings.ConnectionString);
SqlCommand cmd = new SqlCommand("SELECT PasswordHash FROM Users WHERE UserName = @userName", conn);
//Usage of Sql parameters also helps avoid SQL Injection attacks.
cmd.Parameters.Add("@userName", SqlDbType.VarChar, 20);
cmd.Parameters["@userName"].Value = suppliedUserName;
try
{
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
reader.Read(); // Advance to the one and only row
// Return output parameters from returned data stream
string dbPasswordHash = reader.GetString(0);
int saltSize = 5;
string salt = dbPasswordHash.Substring(dbPasswordHash.Length - saltSize);
reader.Close();
// Now take the password supplied by the user
// and generate the hash.
string hashedPasswordAndSalt = CreatePasswordHash(suppliedPassword, salt);
// Now verify them.
passwordMatch = hashedPasswordAndSalt.Equals(dbPasswordHash);
}
catch (Exception ex)
{
throw new Exception("Execption verifying password. " + ex.Message);
}
finally
{
conn.Close();
}
return passwordMatch;
}
protected void btnLogon_Click(object sender, EventArgs e)
{
bool passwordVerified = false;
try
{
passwordVerified = VerifyPassword(txtUserName.Text, txtPassword.Text);
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
return;
}
if (passwordVerified == true)
{
// The user is authenticated
// At this point, an authentication ticket is normally created
// This can subsequently be used to generate a GenericPrincipal
// object for .NET authorization purposes
// For details, see "How To: Use Forms authentication with
// GenericPrincipal objects
lblMessage.Text = "Logon successful: User is authenticated";
}
else
{
lblMessage.Text = "Invalid username or password";
}
}
}