0

My C# console app actually prompts the user for a password and process the password as a string:

Console.Write("Please enter your password: ");
string password = GetPassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);

Is there a way to modify the code to use a secure password (reducing the risk of having the password be exposed throught a memory dump)?

I would like something like:

Console.Write("Please enter your password: ");
SecureString password = GetSecurePassword();
securePassword = GetSecurePassword();
var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
password.Dispose();

Below a complete sample the compiles under Visual Studio 2015:

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

namespace PasswordTest
{
    internal class Program
    {
        public static byte[] GenerateSalt()
        {
            const int saltLength = 32;

            using (var randomNumberGenerator = new RNGCryptoServiceProvider())
            {
                var randomNumber = new byte[saltLength];
                randomNumberGenerator.GetBytes(randomNumber);

                return randomNumber;
            }
        }

        private static byte[] Combine(byte[] first, byte[] second)
        {
            var ret = new byte[first.Length + second.Length];

            Buffer.BlockCopy(first, 0, ret, 0, first.Length);
            Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);

            return ret;
        }

        public static byte[] HashPasswordWithSalt(byte[] toBeHashed, byte[] salt)
        {
            using (var sha256 = SHA256.Create())
            {
                return sha256.ComputeHash(Combine(toBeHashed, salt));
            }
        }

        private static string GetPassword()
        {
            // skip here the code that prompts the password
            // to make simple test that just return a password
            return "mypa55w0rd";
        }

        // Here the code that would normally be used to prompt for a password 
        // and return a SecureString
        public static SecureString GetSecurePassword()
        {
            var password = new SecureString();

            // get the first character of the password
            var nextKey = Console.ReadKey(true);

            while (nextKey.Key != ConsoleKey.Enter)
            {
                if (nextKey.Key == ConsoleKey.Backspace)
                {
                    if (password.Length > 0)
                    {
                        password.RemoveAt(password.Length - 1);

                        // erase the last * as well
                        Console.Write(nextKey.KeyChar);
                        Console.Write(" ");
                        Console.Write(nextKey.KeyChar);
                    }
                }
                else
                {
                    password.AppendChar(nextKey.KeyChar);
                    Console.Write("*");
                }

                nextKey = Console.ReadKey(true);
            }

            Console.WriteLine();

            // lock the password down
            password.MakeReadOnly();
            return password;
        }

        private static void Main(string[] args)
        {
            // Generate the salt
            var salt = GenerateSalt();

            // Hash a password stored in a string variable 
            // Issue: can be exposed through a memory dump
            var password = GetPassword();
            var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);

            // How to hash a password stored in a securestring so that the code could become
            // something like:
            /*
            Console.Write("Please enter your password: ");
            SecureString password = GetSecurePassword();
            var hashedPassword = HashPasswordWithSalt(Encoding.UTF8.GetBytes(password), salt);
            password.Dispose();
            */

            Console.WriteLine("Hashed Password = " + Convert.ToBase64String(hashedPassword));
            Console.ReadLine();
        }
    }
}
Less White
  • 561
  • 4
  • 13
  • I managed to do something similar some time ago. What I did was create a custom Stream and allow reading from it one char at a time. This way, the password is never a string but chars read by one, so it's never fully decrypted in memory. – Camilo Terevinto Sep 09 '17 at 18:11
  • Thanks for your feedback. I will start looking your way. Do you have a short sample I could start from? – Less White Sep 09 '17 at 18:16
  • 1
    When saving a password verifier just using a hash function is not sufficient and just adding a salt does little to improve the security. Instead iterate over an HMAC with a random salt for about a 100ms duration and save the salt with the hash. Use a function such as `PBKDF2`, `Rfc2898DeriveBytes`, `password_hash`, `Bcrypt`, `passlib.hash` or similar functions. The point is to make the attacker spend a substantial of time finding passwords by brute force. – zaph Sep 09 '17 at 18:53
  • 1
    Thanks for the feedback, I will change HashPasswordWithSalt to become: public static byte[] HashPasswordWithSalt(byte[] toBeHashed, byte[] salt, int numberOfRounds) { using (var rfc2898 = new Rfc2898DeriveBytes(toBeHashed, salt, numberOfRounds)) { return rfc2898.GetBytes(32); } } – Less White Sep 09 '17 at 19:41

0 Answers0