[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1207
  • Last Modified:

Salt + Hash Password

Hello Experts,

I have the following code attached and I need a way to SALT and HASH the password in my DB. I also need to authenticate the user based on the SALT and HASH stored in the DB. I'm confused as to the best method to handle this. Do i need to store the SALTED and HASHED Password in the same field in my DB? Or do I store the HASHED Password in it's own field labeled users_password and then store the SALTED value in another field labeled users_password_salted?

I would like to use SHA512 and you will see what I have already which works fine and authenticate fine but I don't know if I"m storing the SALT and HASH correctly and also not sure if I should be using GUID. I found a tutorial that should using GUID but also found another tutorial that explained everything differently which I liked this tutorials explaination but don't know how to implement it. That tutorial is listed below.

I'm also not sure what field type and size I should be using. I'm using varchar(50) now but not sure if that is correct to use.

http://crackstation.net/hashing-security.html


protected void btn_NewUser_Click(object sender, EventArgs e)
    {
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_InsertUsers";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.AddWithValue("@c_id", SqlDbType.Int).Value = ddlClient.SelectedItem.Value;
            cmd.Parameters.AddWithValue("@bldg_id", SqlDbType.Int).Value = ddlBldgLocation.SelectedItem.Value;
            cmd.Parameters.AddWithValue("@s_id", SqlDbType.Int).Value = ddlState.SelectedItem.Value;
            cmd.Parameters.AddWithValue("@users_flname", SqlDbType.VarChar).Value = txtName.Text;
            cmd.Parameters.AddWithValue("@users_street_address", SqlDbType.VarChar).Value = txtHomeAddress.Text;
            cmd.Parameters.AddWithValue("@users_city", SqlDbType.VarChar).Value = txtCity.Text;
            cmd.Parameters.AddWithValue("@users_zip", SqlDbType.VarChar).Value = txtZipCode.Text;
            cmd.Parameters.AddWithValue("@users_phone", SqlDbType.VarChar).Value = txtHomePhone.Text;
            cmd.Parameters.AddWithValue("@users_work_ext", SqlDbType.VarChar).Value = txtWorkExtension.Text;
            cmd.Parameters.AddWithValue("@users_email", SqlDbType.VarChar).Value = txtEmailAddress.Text;
            cmd.Parameters.AddWithValue("@users_username", SqlDbType.VarChar).Value = txtUserName.Text;

            //Create instance of SHA512CryptoServiceProvider
            SHA512CryptoServiceProvider SHA512Hash = new SHA512CryptoServiceProvider();

            byte[] hashedBytes = null;
            UTF8Encoding encoder = new UTF8Encoding();

            string guid = System.Guid.NewGuid().ToString(); //36 long characters
            hashedBytes = SHA512Hash.ComputeHash(encoder.GetBytes(txtPassword.Text + guid));

            cmd.Parameters.AddWithValue("@users_password", SqlDbType.VarChar).Value = hashedBytes;
            cmd.Parameters.AddWithValue("@users_password_salt", SqlDbType.VarChar).Value = guid;

            try
            {
                conn.Open();

                cmd.ExecuteNonQuery();

                Response.Redirect("newuser_success.aspx");
            }

            catch (Exception ex)
            {
                btn_NewUser.Enabled = true;
                lblInsertError.Text = ex.Message.ToString();
            }

            finally
            {
                conn.Close();
            }
        }
    }

Open in new window

0
asp_net2
Asked:
asp_net2
  • 25
  • 25
1 Solution
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello asp_net2, you can accomplish that without using a guid, instead you can use your internal user id (if there is one) plus some static salt to simplify things, also I recommend you to split the user creation and the set password logic.

I can help you with your design, can you post the HealthCourses_InsertUsers code and the definition of the table where the data is inserted?
0
 
asp_net2Author Commented:
Hi yv989c,

First of all thank you very much for helping me with this post. I'm not all that knowledgeable with Salting + Hashing hence the reason I'm here, but hope to learn a thing or to :)

I'm attaching the Stored Procedure, you already have the CODEBEHIND that I'm using above.

However, I'm not sure what you are suggesting but I would like to possible stick to the way it was mentioned in the URL here http://crackstation.net/hashing-security.html only because that makes some sense to me. Could you explain why your method may be better than the one in the URL? I'm only asking because I don't know and this is confusing me because I thought there was only one correct way to handle the Salt + Hash which is what I thought that URL was trying to explain.
ALTER PROCEDURE [dbo].[HealthCourses_InsertUsers]

(
@c_id int,
@bldg_id int,
@s_id int,
@users_flname varchar(50),
@users_street_address varchar(50),
@users_city varchar(50),
@users_zip varchar(5),
@users_phone varchar(12),
@users_work_ext varchar(4),
@users_email varchar(100),
@users_username varchar(50),
@users_password varchar(50),
@users_password_salt varchar(50)
)

AS

INSERT dbo.HealthCourses_Users (c_id, bldg_id, s_id, users_flname, users_street_address, users_city, users_zip, users_phone, users_work_ext, users_email, users_username, users_password, users_password_salt)

VALUES (@c_id, @bldg_id, @s_id, @users_flname, @users_street_address, @users_city, @users_zip, @users_phone, @users_work_ext, @users_email, @users_username, @users_password, @users_password_salt)

Open in new window

0
 
asp_net2Author Commented:
@yv989c,

Once again thank you so much for willing to help. I have been seeing different things on the net when I google Salt + Hash passwords but the URL that I originally posted makes the most sense just not sure how it compares to how I did it nor I'm not sure how to implement what the URL mentions into my code.
0
Prepare for your VMware VCP6-DCV exam.

Josh Coen and Jason Langer have prepared the latest edition of VCP study guide. Both authors have been working in the IT field for more than a decade, and both hold VMware certifications. This 163-page guide covers all 10 of the exam blueprint sections.

 
asp_net2Author Commented:
Sorry, also do I need to store the SALT + HASH into the same field into the DB, or keep the SALT + HASH in two separate fields in the DB?

For example:
users_password  varchar(50)  <--- HASH data goes here.
users_password_salt  varchar(50)  <--- SALT for Password goes here.

Also, am I using varchar or should I be using a different field type?
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hi asp_net2, first I want to know that you understand why you are implementing user password hashing in your application, it is only to protect your user password in case that your DB is compromised, if you hash it with the correct technique will be very hard for an attacker to retrieve it.

About your last question, you need to have your password hash in a field, and the data that you will use as salt wherever you want, the better way to explain this is with an example:
// This common function will generate your password plus added salt.
string GetMyPasswordAndSaltData(int userId, string password)
{
    return userId + "MyP@$sw0rd" + newPassword + userId + "AnotherS@alt";
}

// To save your hashed user password
void SetPassword(string userName, string newPassword)
{
    // Get the internal id of userName (GetMyUserId is a sample method), I going to use this info as part of my salt, it is important that the data you will use as part of your salt is not public.
    int userId = GetMyUserId(userName);

    // Create instance of SHA512CryptoServiceProvider, it will generate your hash
    SHA512CryptoServiceProvider SHA512Hash = new SHA512CryptoServiceProvider();

    // Get my salted password
    string myPasswordPlusSalt = GetMyPasswordAndSaltData(userId, newPassword);

    // Use the SHA512Hash algorithm to hash my salted password
    byte[] myPasswordHash = SHA512Hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(myPasswordPlusSalt));

    // Save my salted password hash to DB (SaveMyHashedPasswordToDb is a sample method)
    SaveMyHashedPasswordToDb(userId, myPasswordHash);
}

// You can use this function to authenticate your user name and password
bool IsValidPassword(string userName, string password)
{
    // Get the internal id of userName (GetMyUserId is a sample method)
    int userId = GetMyUserId(userName);

    // Get the salted password
    string myPasswordPlusSalt = GetMyPasswordAndSaltData(userId, password);

    // Use the SHA512Hash algorithm to hash the salted password
    byte[] myPasswordHash = SHA512Hash.ComputeHash(System.Text.Encoding.UTF8.GetBytes(myPasswordPlusSalt));

    // Retrieve the hash saved in your DB for that user (GetMySavedHashFromDb is a sample method).
    byte[] mySavedPasswordHash = GetMySavedHashFromDb(userId);

    // If both arrays are equals then the password match your saved password, the function will return true.
    return BitConverter.ToString(mySavedPasswordHash) == BitConverter.ToString(myPasswordHash);
}

Open in new window


I have wrote the previous code on the fly so it can contain typos, is just to help you to understand better how to work with user passwords and hashing.

Also, am I using varchar or should I be using a different field type?
You hash is a byte array, so the correct way to save it in your DB is with the binary sql data type.

Now tell me, what represent the column c_id in your HealthCourses_Users table? that is your unique user id?
0
 
asp_net2Author Commented:
Hi yv989c,

Ok, can you elaborate with the URL that I supplied. I would like to use that technique instead of it's ok. Would you be able to assist with that? Also, is there something I did wrong and if so what in my original code?

Also, should I be using VarBinary() and if so what size should I assign to it, 64, 128, 256?
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello, ok no problem, right now I'm busy, I will able to give you a hand at the end of day, ok?
0
 
asp_net2Author Commented:
Hi yv989c,

Ok, that would be great. Thank you so much!!! Like I said before what I originally did works fine but I'm not sure if it's the correct way to SALT + HASH. So I googled SALT + HASH Passwords and found that article. By the sounds of that article it makes sense to me which is why I would like to implement what that article is referring to but not sure how :(

Thanks again yv989c, can't wait to see what you come up with later.

thanks in advance!!!
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello asp_net2, I have done some fixes and modified your code to use the example showed in the article that you have provided, first the DB objects (TSQL):

Update your [dbo].[HealthCourses_InsertUsers] SP with this:
ALTER PROCEDURE
	[dbo].[HealthCourses_InsertUsers]
(
	@c_id int,
	@bldg_id int,
	@s_id int,
	@users_flname varchar(50),
	@users_street_address varchar(50),
	@users_city varchar(50),
	@users_zip varchar(5),
	@users_phone varchar(12),
	@users_work_ext varchar(4),
	@users_email varchar(100),
	@users_username varchar(50),
	@users_password varchar(128)
)
AS

INSERT
	dbo.HealthCourses_Users
(
	c_id,
	bldg_id,
	s_id,
	users_flname, 
	users_street_address, 
	users_city, 
	users_zip, 
	users_phone, 
	users_work_ext, 
	users_email, 
	users_username, 
	users_password--< YOU MUST CHANGE IT IN YOUR TABLE TO VARCHAR(128)
	-- [users_password_salt] <-- YOU MUST DROP THIS COLUMN
)
VALUES
(
	@c_id,
	@bldg_id, 
	@s_id, 
	@users_flname, 
	@users_street_address, 
	@users_city, 
	@users_zip, 
	@users_phone, 
	@users_work_ext, 
	@users_email, 
	@users_username, 
	@users_password
)

Open in new window


Create this new SP:
CREATE PROCEDURE
	[dbo].[HealthCourses_GetUserPassword]
(
	@users_username varchar(50)
)
AS

SELECT
	users_password
FROM
	dbo.HealthCourses_Users
WHERE
	users_username = @users_username

Open in new window



Now the C# code, this contain the PasswordHash class in the article, your posted method and the new IsValidPassword method to help you authenticate your users:
protected void btn_NewUser_Click(object sender, EventArgs e)
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
    {
        SqlCommand cmd = new SqlCommand();
        cmd.CommandText = "dbo.HealthCourses_InsertUsers";
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Connection = conn;

        cmd.Parameters.Add("@c_id", SqlDbType.Int).Value = ddlClient.SelectedItem.Value;
        cmd.Parameters.Add("@bldg_id", SqlDbType.Int).Value = ddlBldgLocation.SelectedItem.Value;
        cmd.Parameters.Add("@s_id", SqlDbType.Int).Value = ddlState.SelectedItem.Value;
        cmd.Parameters.Add("@users_flname", SqlDbType.VarChar, 50).Value = txtName.Text;
        cmd.Parameters.Add("@users_street_address", SqlDbType.VarChar, 50).Value = txtHomeAddress.Text;
        cmd.Parameters.Add("@users_city", SqlDbType.VarChar, 50).Value = txtCity.Text;
        cmd.Parameters.Add("@users_zip", SqlDbType.VarChar, 5).Value = txtZipCode.Text;
        cmd.Parameters.Add("@users_phone", SqlDbType.VarChar, 12).Value = txtHomePhone.Text;
        cmd.Parameters.Add("@users_work_ext", SqlDbType.VarChar, 4).Value = txtWorkExtension.Text;
        cmd.Parameters.Add("@users_email", SqlDbType.VarChar, 100).Value = txtEmailAddress.Text;
        cmd.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = txtUserName.Text;

        // Be sure to validate the password rules first (length, complexity, etc.), this code as is allow blank passwords.
        // Also, your column [users_password] must be of type char(128) or varchar(128) and your SP parameter too.
        cmd.Parameters.Add("@users_password", SqlDbType.VarChar, 128).Value = PasswordHash.HashPassword(txtPassword.Text);

        // You no longer need the [users_password_salt] column in your dbo.HealthCourses_Users table, because the PasswordHash class handle it automaticaly in a single string (salt + hash).
        // cmd.Parameters.Add("@users_password_salt", SqlDbType.VarChar).Value = guid;

        try
        {
            conn.Open();

            cmd.ExecuteNonQuery();

            Response.Redirect("newuser_success.aspx");
        }
        catch (Exception ex)
        {
            btn_NewUser.Enabled = true;
            lblInsertError.Text = ex.Message.ToString();
        }
        finally
        {
            conn.Close();
        }
    }
}


// Method to check your user credentials.
private bool IsValidPassword(string userName, string password)
{
    string correctHash = null;
    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
    {
        SqlCommand cm = new SqlCommand("[dbo].[HealthCourses_GetUserPassword]", conn);
        cm.CommandType = CommandType.StoredProcedure;
        cm.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = userName;
        conn.Open();
        correctHash = cm.ExecuteScalar() as string;
    }

    if (correctHash == null)
    {
        // User not found.
        return false;
    }
    else
    {
        return PasswordHash.ValidatePassword(password, correctHash);
    }
}



/*
    * PasswordHash - A salted password hashing library
    * WWW: https://defuse.ca/
    * Use:
    *      Use 'HashPassword' to create the initial hash, store that in your DB
    *      Then use 'ValidatePassword' with the hash from the DB to verify a password
    *      NOTE: Salting happens automatically, there is no need for a separate salt field in the DB
    */
class PasswordHash
{
    /// <summary>
    /// Hashes a password
    /// </summary>
    /// <param name="password">The password to hash</param>
    /// <returns>The hashed password as a 128 character hex string</returns>
    public static string HashPassword(string password)
    {
        string salt = GetRandomSalt();
        string hash = Sha256Hex(salt + password);
        return salt + hash;
    }

    /// <summary>
    /// Validates a password
    /// </summary>
    /// <param name="password">The password to test</param>
    /// <param name="correctHash">The hash of the correct password</param>
    /// <returns>True if password is the correct password, false otherwise</returns>
    public static bool ValidatePassword(string password, string correctHash)
    {
        if (correctHash.Length < 128)
            throw new ArgumentException("correctHash must be 128 hex characters!");
        string salt = correctHash.Substring(0, 64);
        string validHash = correctHash.Substring(64, 64);
        string passHash = Sha256Hex(salt + password);
        return string.Compare(validHash, passHash) == 0;
    }

    //returns the SHA256 hash of a string, formatted in hex
    private static string Sha256Hex(string toHash)
    {
        SHA256Managed hash = new SHA256Managed();
        byte[] utf8 = UTF8Encoding.UTF8.GetBytes(toHash);
        return BytesToHex(hash.ComputeHash(utf8));
    }

    //Returns a random 64 character hex string (256 bits)
    private static string GetRandomSalt()
    {
        RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
        byte[] salt = new byte[32]; //256 bits
        random.GetBytes(salt);
        return BytesToHex(salt);
    }

    //Converts a byte array to a hex string
    private static string BytesToHex(byte[] toConvert)
    {
        StringBuilder s = new StringBuilder(toConvert.Length * 2);
        foreach (byte b in toConvert)
        {
            s.Append(b.ToString("x2"));
        }
        return s.ToString();
    }
}

Open in new window


Also I have noted that you have created your parameters in a wrong way, the AddWithValue method only need the parameters name and the value (but you were passing a SqlDbType enum as value), I recommend you to use the technique that I show you here to create your parameters in the future.

See the code, test it and give me your thoughts.

I hope this help.
0
 
asp_net2Author Commented:
LOL, are you the owner of the site you attached in your post? If so, then I'm truly impressed. Also, we must use the same laptop ASUS G51? That's what I use in the office, however, looking into HP EliteBook now, long story :)

Anyway, back to the post :) I made all adjustments like you mentiond but not sure how to implement the following code I have attached. All other changes have been made but I cannot test yet until you stear me into the direction to getting the other code going :(

I tried to create a Class.cs and named it PasswordHash but I must have gotten mixed up with those pesky curly brackets because I had a bunch of red line near them :)

Also, I'm a little confused on what you did if you would not mind explaining as if you where to a 5th grader so that I understand :) Still very new to cryptography. Also, did you look at what I did? If so, what did you think about what I did? Would it be okay to use?
/*
    * PasswordHash - A salted password hashing library
    * WWW: https://defuse.ca/
    * Use:
    *      Use 'HashPassword' to create the initial hash, store that in your DB
    *      Then use 'ValidatePassword' with the hash from the DB to verify a password
    *      NOTE: Salting happens automatically, there is no need for a separate salt field in the DB
    */
class PasswordHash
{
    /// <summary>
    /// Hashes a password
    /// </summary>
    /// <param name="password">The password to hash</param>
    /// <returns>The hashed password as a 128 character hex string</returns>
    public static string HashPassword(string password)
    {
        string salt = GetRandomSalt();
        string hash = Sha256Hex(salt + password);
        return salt + hash;
    }

    /// <summary>
    /// Validates a password
    /// </summary>
    /// <param name="password">The password to test</param>
    /// <param name="correctHash">The hash of the correct password</param>
    /// <returns>True if password is the correct password, false otherwise</returns>
    public static bool ValidatePassword(string password, string correctHash)
    {
        if (correctHash.Length < 128)
            throw new ArgumentException("correctHash must be 128 hex characters!");
        string salt = correctHash.Substring(0, 64);
        string validHash = correctHash.Substring(64, 64);
        string passHash = Sha256Hex(salt + password);
        return string.Compare(validHash, passHash) == 0;
    }

    //returns the SHA256 hash of a string, formatted in hex
    private static string Sha256Hex(string toHash)
    {
        SHA256Managed hash = new SHA256Managed();
        byte[] utf8 = UTF8Encoding.UTF8.GetBytes(toHash);
        return BytesToHex(hash.ComputeHash(utf8));
    }

    //Returns a random 64 character hex string (256 bits)
    private static string GetRandomSalt()
    {
        RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
        byte[] salt = new byte[32]; //256 bits
        random.GetBytes(salt);
        return BytesToHex(salt);
    }

    //Converts a byte array to a hex string
    private static string BytesToHex(byte[] toConvert)
    {
        StringBuilder s = new StringBuilder(toConvert.Length * 2);
        foreach (byte b in toConvert)
        {
            s.Append(b.ToString("x2"));
        }
        return s.ToString();
    }
}

Open in new window

0
 
asp_net2Author Commented:
Hi yv989c,

Ok, sorry, I was able to get everything you posted above to work. But only small problem, well, I guess it's a big problem, I can't login now :) :) I'm not sure how to implement your IsValidPassword Event Handler Code with what I'm currently using now. Also, I'm using FormsAuthentication if that matters.

Once again, now sure how to make your code below work with what I have attached.

// Method to check your user credentials.
private bool IsValidPassword(string userName, string password)
{
    string correctHash = null;
    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
    {
        SqlCommand cm = new SqlCommand("[dbo].[HealthCourses_GetUserPassword]", conn);
        cm.CommandType = CommandType.StoredProcedure;
        cm.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = userName;
        conn.Open();
        correctHash = cm.ExecuteScalar() as string;
    }

    if (correctHash == null)
    {
        // User not found.
        return false;
    }
    else
    {
        return PasswordHash.ValidatePassword(password, correctHash);
    }
}
CODEBEHIND:

protected void btn_ProgramInfoSignIn_Click(object sender, EventArgs e)
    {
        //Retrieve the guid from db
        string guid = String.Empty;

        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "HealthCourses_LoginPassSalt";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Connection = conn;

            cmd.Parameters.AddWithValue("@users_username", SqlDbType.VarChar).Value = txtUserName.Text;

            DataTable dtGuid = new DataTable();

            SqlDataAdapter adp = new SqlDataAdapter();
            adp.SelectCommand = cmd;
            adp.Fill(dtGuid);

            if (dtGuid != null && dtGuid.Rows.Count > 0)
            {
                guid = dtGuid.Rows[0]["users_password_salt"].ToString();

                SqlCommand cmdClientLogin = new SqlCommand();
                cmdClientLogin.CommandText = "HealthCourses_Login";
                cmdClientLogin.CommandType = CommandType.StoredProcedure;
                cmdClientLogin.Connection = conn;

                //Create instance of SHA512CryptoServiceProvider
                SHA512CryptoServiceProvider SHA512Hash = new SHA512CryptoServiceProvider();
                byte[] hashedBytes = null;
                UTF8Encoding encoder = new UTF8Encoding();
                hashedBytes = SHA512Hash.ComputeHash(encoder.GetBytes(txtPassword.Text + guid));

                cmdClientLogin.Parameters.AddWithValue("@users_username", SqlDbType.VarChar).Value = txtUserName.Text;
                cmdClientLogin.Parameters.AddWithValue("@users_password", SqlDbType.VarChar).Value = hashedBytes;

                conn.Open();

                SqlDataReader rdr = cmdClientLogin.ExecuteReader();

                if (rdr.HasRows && rdr.Read())
                {
                    rdr.Close();
                    conn.Close();
                    Session["UserNameSessionID"] = txtUserName.Text;
                    FormsAuthentication.RedirectFromLoginPage(txtPassword.Text, false);
                }
            }

            else
            {
                lblSignInError.Text = "Invalid Credentials!";
            }
        }
    }

Open in new window

0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello buddy, no problem, use this in your authentication page:
// Method to check your user credentials.
private bool IsValidPassword(string userName, string password)
{
    string correctHash = null;
    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
    {
        SqlCommand cm = new SqlCommand("[dbo].[HealthCourses_GetUserPassword]", conn);
        cm.CommandType = CommandType.StoredProcedure;
        cm.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = userName;
        conn.Open();
        correctHash = cm.ExecuteScalar() as string;
    }

    if (correctHash == null)
    {
        // User not found.
        return false;
    }
    else
    {
        return PasswordHash.ValidatePassword(password, correctHash);
    }
}

// Your sign in method
protected void btn_ProgramInfoSignIn_Click(object sender, EventArgs e)
{
    if (IsValidPassword(txtUserName.Text, txtPassword.Text))
    {
        Session["UserNameSessionID"] = txtUserName.Text;
        FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, false);
    }
    else
    {
        lblSignInError.Text = "Invalid Credentials!";
    }
}

Open in new window


Also be sure to put the PasswordHash class file in your aspnet App_Code folder, so it can be accessed by all your pages, and dont forget to create the new SP HealthCourses_GetUserPassword in your DB.
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hey and stop using AddWithValue method for your parameters, use the technique that I show you here, always be explicit:
cmd.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = txtUserName.Tex;

Open in new window


But if you still want to use the AddWithValue method, do it this way:

Correct:
cmd.Parameters.AddWithValue("@users_username", txtUserName.Tex);

Open in new window


Wrong:
cmd.Parameters.AddWithValue("@users_username", SqlDbType.VarChar).Value = txtUserName.Text;

Open in new window


Reference:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlparametercollection.addwithvalue.aspx
0
 
asp_net2Author Commented:
Hi yv989c,

Ok, I believe I made all the changes correctly for the login page but when I try to login with either correct or not correct credentials I keep getting the following error message below. I'm going to attach my CodeBehind and SP for the Login page.

ERROR IS FROM THE PasswordHash.cs file:
string salt = correctHash.Substring(0, 64);

ERROR MESSAGE:
correctHash must be 128 hex characters!
PasswordHash.cs:

using System;
using System.Security.Cryptography;
using System.Text;

/// <summary>
/// Summary description for PasswordHash
/// </summary>
public class PasswordHash
{
    /// <summary>
    /// Hashes a password
    /// </summary>
    /// <param name="password">The password to hash</param>
    /// <returns>The hashed password as a 128 character hex string</returns>
    public static string HashPassword(string password)
    {
        string salt = GetRandomSalt();
        string hash = Sha256Hex(salt + password);
        return salt + hash;
    }

    /// <summary>
    /// Validates a password
    /// </summary>
    /// <param name="password">The password to test</param>
    /// <param name="correctHash">The hash of the correct password</param>
    /// <returns>True if password is the correct password, false otherwise</returns>
    public static bool ValidatePassword(string password, string correctHash)
    {
        if (correctHash.Length < 128)
            throw new ArgumentException("correctHash must be 128 hex characters!");
        string salt = correctHash.Substring(0, 64);
        string validHash = correctHash.Substring(64, 64);
        string passHash = Sha256Hex(salt + password);
        return string.Compare(validHash, passHash) == 0;
    }

    //returns the SHA256 hash of a string, formatted in hex
    private static string Sha256Hex(string toHash)
    {
        SHA256Managed hash = new SHA256Managed();
        byte[] utf8 = UTF8Encoding.UTF8.GetBytes(toHash);
        return BytesToHex(hash.ComputeHash(utf8));
    }

    //Returns a random 64 character hex string (256 bits)
    private static string GetRandomSalt()
    {
        RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
        byte[] salt = new byte[32]; //256 bits
        random.GetBytes(salt);
        return BytesToHex(salt);
    }

    //Converts a byte array to a hex string
    private static string BytesToHex(byte[] toConvert)
    {
        StringBuilder s = new StringBuilder(toConvert.Length * 2);
        foreach (byte b in toConvert)
        {
            s.Append(b.ToString("x2"));
        }
        return s.ToString();
    }
}

Open in new window

LOGIN CODEBEHIND:

// Method to check your user credentials.
    private bool IsValidPassword(string userName, string password)
    {
        string correctHash = null;
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
        {
            SqlCommand cm = new SqlCommand("HealthCourses_GetUserPassword", conn);
            cm.CommandType = CommandType.StoredProcedure;
            cm.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = userName;
            conn.Open();
            correctHash = cm.ExecuteScalar() as string;
        }

        if (correctHash == null)
        {
            // User not found.
            return false;
        }
        else
        {
            return PasswordHash.ValidatePassword(password, correctHash);
        }
    }

    protected void btn_ProgramInfoSignIn_Click(object sender, EventArgs e)
    {
        if (IsValidPassword(txtUserName.Text, txtPassword.Text))
        {
            Session["UserNameSessionID"] = txtUserName.Text;
            FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, false);
        }
        else
        {
            lblSignInError.Text = "Invalid Credentials!";
        }
    }
}

Open in new window

STORED PROCEDURE:

ALTER PROCEDURE [dbo].[HealthCourses_GetUserPassword]
(
	@users_username varchar(50)
)
AS

SELECT
	users_password
FROM
	dbo.HealthCourses_Users
WHERE
	users_username = @users_username

Open in new window

0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello buddy, did you changed the [users_password] column in your table [HealthCourses_Users] to varchar(128) as I indicated?
0
 
asp_net2Author Commented:
Yes
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Ok, I forget to mention that you will need to reset the password for all the existent users by using the new code.
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Test the code with a new user to see if everything work as expected.
0
 
asp_net2Author Commented:
Oh, shoot. I'm very sorry yv989c. I forgot to change users_password from 50 to 128. Sorry, it's workig fine now :)

Also, is it possible to crack this method? Not that I want to but just curious if an experience hacker could crack it.
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Of course yes, really I don't like that example, it is handy but all the secrets required to crack your password are saved in your table, if your DB is compromised an experience hacker can easily crack it... Do you want a demo? to make it simple, set a password with only one character A-Z, then send me back your password hash, I will tell you what is your password ;)
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Oh, but now I see that it can be easily modified to make it very hard to crack
0
 
asp_net2Author Commented:
Hi yv989c,

I created a password with one character and the reset are numeric. Good luck :)

I would love a demo that would show how to do that :)

Also, what did you mean by "Oh, but now I see that it can be easily modified to make it very hard to crack "?

Password to crack :)
ee5f62975c99626ad223940314534fb7cc6fd8c5f91a81f64b72c601ca4d9fc3739a0db9402c54a2f424b9a5d2d906fc7797ffa2c026c46df7fd8f2ace57bb26
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello, I dont understand when you say "and the reset are numeric.", to find it quick I only need one character.
0
 
asp_net2Author Commented:
:) sorry the first character is a capital J
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
I have explained wrong, to decode it fast I need a hash of a password that is formed by only one letter :p I want to show you that if I got the salt I can try to decode your password by using a simple dictionary attack.
0
 
asp_net2Author Commented:
How do I just get you a HASH?
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
J9090 ;)
0
 
asp_net2Author Commented:
Your freaking amazing..... How the hell did you do that ???? You are a Wizard :)

I thought this was susspose to be secure :(
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Use this improved PasswordHash class and you not have to worry about that:
class PasswordHash
{
    /// <summary>
    /// Hashes a password
    /// </summary>
    /// <param name="password">The password to hash</param>
    /// <returns>The hashed password as a 128 character hex string</returns>
    public static string HashPassword(string password)
    {
        string salt = GetRandomSalt();
        string hash = Sha256Hex(salt + SecretSalt(password));
        return salt + hash;
    }

    // Secret salt, if your DB is compromised, a hacker will need this info to be able to decode your password.
    private static string SecretSalt(string password)
    {
        // Change the text bellow to something that only you will know, otherwise I will be able to decode your password again ;)
        return "ThisWillB3UAn0th3r$3cret" + password + "Sav3d1nY0urPasswordHashCla$$";
    }

    /// <summary>
    /// Validates a password
    /// </summary>
    /// <param name="password">The password to test</param>
    /// <param name="correctHash">The hash of the correct password</param>
    /// <returns>True if password is the correct password, false otherwise</returns>
    public static bool ValidatePassword(string password, string correctHash)
    {
        if (correctHash.Length < 128)
            throw new ArgumentException("correctHash must be 128 hex characters!");

        string salt = correctHash.Substring(0, 64);
        string validHash = correctHash.Substring(64, 64);
        string passHash = Sha256Hex(salt + SecretSalt(password));

        return string.Compare(validHash, passHash) == 0;
    }

    //returns the SHA256 hash of a string, formatted in hex
    private static string Sha256Hex(string toHash)
    {
        SHA256Managed hash = new SHA256Managed();
        byte[] utf8 = UTF8Encoding.UTF8.GetBytes(toHash);
        return BytesToHex(hash.ComputeHash(utf8));
    }

    //Returns a random 64 character hex string (256 bits)
    private static string GetRandomSalt()
    {
        RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
        byte[] salt = new byte[32]; //256 bits
        random.GetBytes(salt);
        return BytesToHex(salt);
    }

    //Converts a byte array to a hex string
    private static string BytesToHex(byte[] toConvert)
    {
        StringBuilder s = new StringBuilder(toConvert.Length * 2);
        foreach (byte b in toConvert)
        {
            s.Append(b.ToString("x2"));
        }
        return s.ToString();
    }
}

Open in new window


As you can see I have created this method:
// Secret salt, if your DB is compromised, a hacker will need this info to be able to decode your password.
private static string SecretSalt(string password)
{
    // Change the text bellow to something that only you will know, otherwise I will be able to decode your password again ;)
    return "ThisWillB3UAn0th3r$3cret" + password + "Sav3d1nY0urPasswordHashCla$$";
}

Open in new window


The only way (but theoretically is not impossible) that somebody can decode a user password stored in your db is that both systems are compromised (db and web). Also window has places where you can store your secret salt but it will require more work.
0
 
asp_net2Author Commented:
What is the most secure method if you where storing top secret information. I'm a little worried that what we are doing is not as secure as I thought was. What is more secure than what we are doing now? If there is something better than I need that. Pretend as if we are the existence of alien life :)

Also, how do you know so much about this stuff, I would love to learn and know as much as you and would live to know what material you are studying . People who work with you are very lucky!!!

So is there a more top secret approach to storing data without the worry of your data being decoded? If so I would be willing to create a new post just for that.

Also, why do I need to create a password like you are mentioning above? Where will that be stored at, and how much more secure does that make it?
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Wow, there are too many questions, you can trust in the last code that I have posted, it is secure because not all the data required to verify the hash is stored in your DB, so it will be very difficult for an attacker to use a brute force or dictionary method to decode your password, will need years and much computing power to achieve that, but is important that your secret salt is long enough and also stored in a safe place.

The PasswordHash class as is in the article is not safe, first because that code is public, so an experienced person can guest that you are using it and he can see how it works, second because all the data required to decode your password is stored in your db, but adding the simple SecretSalt method to it do a big deference.

BTW I don't know something 100% secure, I say, if it is man made, then another man can crack it ;)

English is not my first language, so I hope you understand my grammatical errors.
0
 
asp_net2Author Commented:
Ok, what about changing to Sha512 and using a higher field value than 128?

Also, what about storing the salt and hash in two separate fields in the DB like I had initially. Is that more or less secure than storing salt and hash in same DB field?

Also, what about using GUID. Is that less secure to use for generating the salt?

Sorry for so many questions.

No need to apologize for your grammar, you are fine.

Thank you for helping. Wish I had half of your knowledge. I'm sure your coworkers feel blessed to work with someone like you.
0
 
asp_net2Author Commented:
Also, what happens if I change the SecretSalt after I have used it to create passwords? If I change the SecretSalt every 6 months then what happens when I authenticate against the passwords that I used when using the previous SecretSalt?
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
1. If you use the SHA512Managed instead of SHA256Managed is better, that will require more CPU time to process so the time required to decrypt a password is also increased, but of course it require more space in your DB (the field size need to be increased), the PasswordHash class need to be modified to work with the SHA512Managed class, also I prefer to save this data in the DB as bytes instead of strings.

2. Is the same thing, indeed, separating values will make things easier for a hacker.

3. Using the RNGCryptoServiceProvider to build your salt is a better technique (it give you more complexity in your salt) that using a guid.
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
If you change your secret salt all yours passwords need to be recreated, is not a good idea, instead enforce a policy to change your users passwords from time to time.
0
 
asp_net2Author Commented:
Ok, if I use Sha512 then what field size should I use other than 128?

Also, if you where asked to create a secure siite to store passwords than what would you do with the SecretSalt? Would you never change it or would you change it in time?
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
I'm busy right now... I will try to respond at night....
0
 
asp_net2Author Commented:
Hi yv989c,

Ok, thanks. I appreciate your help...
0
 
asp_net2Author Commented:
Hi yv989c,

When you have time could you show me what I would need to have for Step 1 for your comment 37047811?

I would like to use SHA512 and would like to use bytes rather than strings as you mentioned it being better to store in DB. Also, would it be possible to explain how you crackey my password.

Also, I do plan on implementing the SecretSalt as you showed if you don't mind mentioning that again if you could repost the code I need based on Step 1 from your comment 37047811.

I think that you mentioned a very good point in regards to setting up a policy to have users change passwords. That is very good idea, perhaps I can create another post if you would like to help me with that as well.

Thanks in advance!!!
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello asp_net2, I have been busy, but I will help you with this today for sure.
0
 
asp_net2Author Commented:
Hi yv989c,

Ok, thank you. Please take care of what you need to do before helping me with this. I just really appreciate your help and don't want to bother you if your busy. I will wait until you have time. Thank you again!!!
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello buddy, sorry but I could not respond yesterday, here the new class with the Sha512 algorithm implementation:
class PasswordHash
{
    /// <summary>
    /// Secret salt, if your DB is compromised, a hacker will need this info to be able to decode your password.
    /// </summary>
    /// <param name="password"></param>
    /// <returns></returns>
    private static string SecretSalt(string password)
    {
        // Change the text bellow to something that only you will know, otherwise I will be able to decode your password again ;)
        return "ThisWillB3UAn0th3r$3cret" + password + "Sav3d1nY0urPasswordHashCla$$";
    }

    /// <summary>
    /// Hashes a password
    /// </summary>
    /// <param name="password">The password to hash</param>
    /// <returns>The hashed salt + password as 96 bytes of data</returns>
    public static byte[] HashPassword(string password)
    {
        byte[] salt = GetRandomSalt();
        byte[] hash = Sha512Bytes(salt, SecretSalt(password));
        byte[] saltAndhash = new byte[96];
        salt.CopyTo(saltAndhash, 0);
        hash.CopyTo(saltAndhash, 32);
        return saltAndhash;
    }

    /// <summary>
    /// Validates a password
    /// </summary>
    /// <param name="password">The password to test</param>
    /// <param name="correctHash">The hash of the correct password</param>
    /// <returns>True if password is the correct password, false otherwise</returns>
    public static bool ValidatePassword(string password, byte[] correctHash)
    {
        if (correctHash.Length < 96)
            throw new ArgumentException("correctHash must be 96 bytes!");

        byte[] salt = new byte[32];
        Array.Copy(correctHash, salt, 32);

        string validHash = BitConverter.ToString(correctHash, 32, 64);
        string passHash = BitConverter.ToString(Sha512Bytes(salt, SecretSalt(password)));

        return string.Compare(validHash, passHash) == 0;
    }

    //returns the SHA512 hash of a string
    private static byte[] Sha512Bytes(byte[] salt, string toHash)
    {
        SHA512Managed hash = new SHA512Managed();
        byte[] utf8 = UTF8Encoding.UTF8.GetBytes(toHash);
        byte[] data = new byte[salt.Length + utf8.Length];
        salt.CopyTo(data, 0);
        utf8.CopyTo(data, salt.Length);
        return hash.ComputeHash(data);
    }

    //Returns a random 32 bytes (256 bits)
    private static byte[] GetRandomSalt()
    {
        RNGCryptoServiceProvider random = new RNGCryptoServiceProvider();
        byte[] salt = new byte[32]; //256 bits
        random.GetBytes(salt);
        return salt;
    }
}

Open in new window


The updated code for password validation in your login page:
// Method to check your user credentials.
private bool IsValidPassword(string userName, string password)
{
    byte[] correctHash = null;
    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
    {
        SqlCommand cm = new SqlCommand("[dbo].[HealthCourses_GetUserPassword]", conn);
        cm.CommandType = CommandType.StoredProcedure;
        cm.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = userName;
        conn.Open();
        correctHash = cm.ExecuteScalar() as byte[];
    }

    if (correctHash == null)
    {
        // User not found.
        return false;
    }
    else
    {
        return PasswordHash.ValidatePassword(password, correctHash);
    }
}

Open in new window


Updated SP [dbo].[HealthCourses_InsertUsers] (T-SQL code):
[users_password] column YOU MUST CHANGE IT IN YOUR TABLE TO BINARY(96)
ALTER PROCEDURE
	[dbo].[HealthCourses_InsertUsers]
(
	@c_id int,
	@bldg_id int,
	@s_id int,
	@users_flname varchar(50),
	@users_street_address varchar(50),
	@users_city varchar(50),
	@users_zip varchar(5),
	@users_phone varchar(12),
	@users_work_ext varchar(4),
	@users_email varchar(100),
	@users_username varchar(50),
	@users_password binary(96)
)
AS

INSERT
	dbo.HealthCourses_Users
(
	c_id,
	bldg_id,
	s_id,
	users_flname, 
	users_street_address, 
	users_city, 
	users_zip, 
	users_phone, 
	users_work_ext, 
	users_email, 
	users_username, 
	users_password--< YOU MUST CHANGE IT IN YOUR TABLE TO BINARY(96)
)
VALUES
(
	@c_id,
	@bldg_id, 
	@s_id, 
	@users_flname, 
	@users_street_address, 
	@users_city, 
	@users_zip, 
	@users_phone, 
	@users_work_ext, 
	@users_email, 
	@users_username, 
	@users_password
)

Open in new window


Updated btn_NewUser_Click method:
protected void btn_NewUser_Click(object sender, EventArgs e)
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["HealthCourses"].ConnectionString))
    {
        SqlCommand cmd = new SqlCommand();
        cmd.CommandText = "dbo.HealthCourses_InsertUsers";
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Connection = conn;

        cmd.Parameters.Add("@c_id", SqlDbType.Int).Value = ddlClient.SelectedItem.Value;
        cmd.Parameters.Add("@bldg_id", SqlDbType.Int).Value = ddlBldgLocation.SelectedItem.Value;
        cmd.Parameters.Add("@s_id", SqlDbType.Int).Value = ddlState.SelectedItem.Value;
        cmd.Parameters.Add("@users_flname", SqlDbType.VarChar, 50).Value = txtName.Text;
        cmd.Parameters.Add("@users_street_address", SqlDbType.VarChar, 50).Value = txtHomeAddress.Text;
        cmd.Parameters.Add("@users_city", SqlDbType.VarChar, 50).Value = txtCity.Text;
        cmd.Parameters.Add("@users_zip", SqlDbType.VarChar, 5).Value = txtZipCode.Text;
        cmd.Parameters.Add("@users_phone", SqlDbType.VarChar, 12).Value = txtHomePhone.Text;
        cmd.Parameters.Add("@users_work_ext", SqlDbType.VarChar, 4).Value = txtWorkExtension.Text;
        cmd.Parameters.Add("@users_email", SqlDbType.VarChar, 100).Value = txtEmailAddress.Text;
        cmd.Parameters.Add("@users_username", SqlDbType.VarChar, 50).Value = txtUserName.Text;

        // Be sure to validate the password rules first (length, complexity, etc.), this code as is allow blank passwords.
        // Also, your column [users_password] must be of type binary(96) and your SP parameter too.
        cmd.Parameters.Add("@users_password", SqlDbType.Binary, 96).Value = PasswordHash.HashPassword(txtPassword.Text);

        try
        {
            conn.Open();

            cmd.ExecuteNonQuery();

            Response.Redirect("newuser_success.aspx");
        }
        catch (Exception ex)
        {
            btn_NewUser.Enabled = true;
            lblInsertError.Text = ex.Message.ToString();
        }
        finally
        {
            conn.Close();
        }
    }
}

Open in new window


Implement these code in your project and test it, I have not tested the DB code changes, I hope there are no typos.

And as you can see I prefer bytes to store it, requires less DB space, that is because when you store this data as chars you need to convert the bytes to hex code, and each byte representation in hex code requires two bytes, now you can see that we are using a 512 bits (+32 salt) algorithm and indeed we are using less DB space that the original 256 bits (+32 salt) that were stored by using a hex string.

I you have any questions I will try to respond at night, have a good day!
0
 
asp_net2Author Commented:
Hi yv989c,

Thank you, that all worked out perfectly. I'm getting ready to close this post. But first, could you explain how you cracked that password Hash and how I can best protect this code and the values in the DB?

Also, I would like to create another post about password policy but would really like you help with that since you already know my DB schema and have a understanding of my code. If you can't help I understand and perhaps you can point me to some good tutoials that explain how to setup password policy using ASP.NET C# if you can't help. It would be nice to have user change his/her password every 90 days or whatever.
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello again buddy, I was able to easily guess your password because this:
1. I know the code that you have used to hash it.
2. The hash that you provided has the salt in it.
3. You give me a hint about your password, start with the J letter and the rest are numbers, that info help me to do a brute force crack ;)

Here the code that I wrote to guess your password:
void FindPassword()
{
    string seed = "J";
    string pwd = seed;
    int counter = 0;
    while (!PasswordHash.ValidatePassword(pwd, "ee5f62975c99626ad223940314534fb7cc6fd8c5f91a81f64b72c601ca4d9fc3739a0db9402c54a2f424b9a5d2d906fc7797ffa2c026c46df7fd8f2ace57bb26"))
    {
        pwd = seed + counter.ToString();
        counter++;
    }
    Console.WriteLine("Your password is: " + pwd);
}

Open in new window


But what happen if you use a secret salt too? well is going to be almost impossible to me to crack your password, by using my secret salt example (of course you need to change it to something that I dont know) is like I try to guess a 50 char length password with especial characters, I will need tooo many years to crack your password by using the computer power that I have on hand.

About protecting your code..., that is a complex topic, there are many variables, your OS, your environment, your network, etc etc etc... I can't give you a direct answer about that, just keep on mind that your secret (salt) need to be in a safe place, like it is your home keys, if you leave it in a public place, anyone will be able to enter in your house.

If you open a question about password policy's I will participate on it ok.
0
 
asp_net2Author Commented:
Hi yv989c,

Ok, I have to ask this question, who do you work for, I only ask because you are very smart and you still amazed me of what you did and most importantly how knowledable you are :)

I'm a little confused on how I can store that special password safely if it's part of the PasswordHash.cs code. the .cs file is stored in the App_Code folder but is that secure enough? If not, how do you suggest that I store it in a safe place?

Also, for the password cracking you did is that all I would have to do is add that code above along with the PasswordHash.cs file or is there more to it? What Brute Force software did you use?

>> If you open a question about password policy's I will participate on it ok.
I just created a post, please see the URL for it below.

http://www.experts-exchange.com/Programming/Languages/.NET/ASP.NET/Q_27425465.html
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Hello, about your first paragraph I must say "keep hungry stay foolish".

I'm a little confused on how I can store that special password safely if it's part of the PasswordHash.cs code. the .cs file is stored in the App_Code folder but is that secure enough? If not, how do you suggest that I store it in a safe place?
Well, the App_Code folder is a safe folder, aspnet don't allow to download the content of that folder, unless of course some body exploit an aspnet vulnerability that allow to read its content and then you know the rest... I can suggest to save that info (your secret salt) in a section on your web.config file, then you can encrypt that section, aspnet can automatically decrypt the value stored in your web.config file section when you read it, if your web application is compromised the attacker will have everything but he can't read the content of your web.config file because it is encrypted, and the key required to decrypt that info is the machine key, so ALL your computer must be compromised to allow him to decrypt your salt and then your user passwords, I think this is the best method that you can use, of course this is not the only way.
Reference:
http://msdn.microsoft.com/en-us/library/dtkwfdky.aspx


Also, for the password cracking you did is that all I would have to do is add that code above along with the PasswordHash.cs file or is there more to it? What Brute Force software did you use?
Yes, PasswordHash class + The code provided, why I will require something else? the code just try until there is a match, that is the reason it is called brute force.
0
 
asp_net2Author Commented:
Hi yv989c,

ok, last question only and I"m closing post :) If you believe or think that it may be better to store the SecretPassword into the web.config file then can you tell me what I need to do? Also, what about adding the SecretPassword to the Salt + Hash, if it's stored in the web.config then how to I access that when I initially create the new password?
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
asp_net2 .... you are asking too much in this question ;)

If you believe or think that it may be better to store the SecretPassword into the web.config file then can you tell me what I need to do?
I left you a reference: http://msdn.microsoft.com/en-us/library/dtkwfdky.aspx, I can help you with that, I know it is not a simple task if you have never worked with this kind of stuff, but is a good idea to open a new question for this, if I don't have the time I'm sure there is others that can help you with this task.



Also, what about adding the SecretPassword to the Salt + Hash
Bad idea, then stop calling it SecretPassword, remember, you want to hide that secret, if you put it on your DB and then it is compromised you are giving all the info required to crack your password.



if it's stored in the web.config then how to I access that when I initially create the new password?
This is related to the first quote.
0
 
asp_net2Author Commented:
Hi yv989c,

Thank you so very much for all your time, patience, and willing to help others. I can't thank you enough. I really have a passion for stuff like this and working with people like you is AMAZING...

Thank you again yv989c. Not sure where you work at but I'm sure your fellow employees but enjoy working with you daily. I wish that only one day I can be as knowledgable as you so I can help others and pay it forward...
0
 
Carlos VillegasFull Stack .NET DeveloperCommented:
Glad to have been of help buddy
0

Featured Post

Important Lessons on Recovering from Petya

In their most recent webinar, Skyport Systems explores ways to isolate and protect critical databases to keep the core of your company safe from harm.

  • 25
  • 25
Tackle projects and never again get stuck behind a technical roadblock.
Join Now