Design

Design
asp.net mvc

2016年8月2日 星期二

Asp.net mvc Salt Hashing 建立安全的會員註冊

首先為什麼我們需要 Salt Hashing我們的密碼 目的是為了防範資料庫在不慎外洩的情況下密碼也不會透露,那實際該怎做了 主要思路為我們會員在註冊時隨機產生個值我們稱值為salt再加上我們的密碼最後經過hash加密儲存在我們的資料庫,最後在多個欄位儲存我們隨機產生的salt值,因為我們hash是不可逆的所以就算在資料庫外洩的情況下也不會透漏我們真實的密碼,所以其實真正的密碼是如何也只有本人知道,就這是為什麼我們在很多網站忘記密碼時官方選擇給你重新設定新密碼而不是直接給你密碼的原因,現在我們有了hash(salt+password)的值跟salt的值就會下面圖一樣

這樣我們就是在登入時透過使用者輸入的password加上我們資料庫儲存的salt的值再透過hash去比對我們資料庫passwordhash的值是否一樣達到安全的目的



那我們該如何在asp.net mvc 重現呢 如下
首先建立我們的註冊會員model
public class User  
{  
    [Key]  
    public int RegistrationId  
    {  
        get;  
        set;  
    } //This will be primary key column with auto increment  
    public string FirstName  
    {  
        get;  
        set;  
    }  
    public string LastName  
    {  
        get;  
        set;  
    }  
    public string UserName  
    {  
        get;  
        set;  
    }  
    public string EmailId  
    {  
        get;  
        set;  
    }  
    public string Password  
    {  
        get;  
        set;  
    }  
    public string Gender  
    {  
        get;  
        set;  
    }  
    public string VCode  
    {  
        get;  
        set;  
    }  
    public DateTime CreateDate  
    {  
        get;  
        set;  
    }  
    public DateTime ModifyDate  
    {  
        get;  
        set;  
    }  
    public bool Status  
    {  
        get;  
        set;  
    }  
}  
隨自己喜好建立DB 我是用code first migrations update
public class CmsDbContext : DbContext  
{  
    public DbSet ObjRegisterUser { get; set; } // Here User is the class
}  
寫個加密的helper要用的時候可以叫用
public static class Helper  
{  
    public static string ToAbsoluteUrl(this string relativeUrl) //Use absolute URL instead of adding phycal path for CSS, JS and Images     
    {  
        if (string.IsNullOrEmpty(relativeUrl)) return relativeUrl;  
        if (HttpContext.Current == null) return relativeUrl;  
        if (relativeUrl.StartsWith("/")) relativeUrl = relativeUrl.Insert(0, "~");  
        if (!relativeUrl.StartsWith("~/")) relativeUrl = relativeUrl.Insert(0, "~/");  
        var url = HttpContext.Current.Request.Url;  
        var port = url.Port != 80 ? (":" + url.Port) : String.Empty;  
        return String.Format("{0}://{1}{2}{3}", url.Scheme, url.Host, port, VirtualPathUtility.ToAbsolute(relativeUrl));  
    }  
    public static string GeneratePassword(int length) //length of salt    
    {  
        const string allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";  
        var randNum = new Random();  
        var chars = new char[length];  
        var allowedCharCount = allowedChars.Length;  
        for (var i = 0; i <= length - 1; i++)  
        {  
            chars[i] = allowedChars[Convert.ToInt32((allowedChars.Length) * randNum.NextDouble())];  
        }  
        return new string(chars);  
    }  
    public static string EncodePassword(string pass, string salt) //encrypt password    
    {  
        byte[] bytes = Encoding.Unicode.GetBytes(pass);  
        byte[] src = Encoding.Unicode.GetBytes(salt);  
        byte[] dst = new byte[src.Length + bytes.Length];  
        System.Buffer.BlockCopy(src, 0, dst, 0, src.Length);  
        System.Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);  
        HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");  
        byte[] inArray = algorithm.ComputeHash(dst);  
        //return Convert.ToBase64String(inArray);    
        return EncodePasswordMd5(Convert.ToBase64String(inArray));  
    }  
    public static string EncodePasswordMd5(string pass) //Encrypt using MD5    
    {  
        Byte[] originalBytes;  
        Byte[] encodedBytes;  
        MD5 md5;  
        //Instantiate MD5CryptoServiceProvider, get bytes for original password and compute hash (encoded password)    
        md5 = new MD5CryptoServiceProvider();  
        originalBytes = ASCIIEncoding.Default.GetBytes(pass);  
        encodedBytes = md5.ComputeHash(originalBytes);  
        //Convert encoded bytes back to a 'readable' string    
        return BitConverter.ToString(encodedBytes);  
    }  
    public static string base64Encode(string sData) // Encode    
    {  
        try  
        {  
            byte[] encData_byte = new byte[sData.Length];  
            encData_byte = System.Text.Encoding.UTF8.GetBytes(sData);  
            string encodedData = Convert.ToBase64String(encData_byte);  
            return encodedData;  
        }  
        catch (Exception ex)  
        {  
            throw new Exception("Error in base64Encode" + ex.Message);  
        }  
    }  
    public static string base64Decode(string sData) //Decode    
    {  
        try  
        {  
            var encoder = new System.Text.UTF8Encoding();  
            System.Text.Decoder utf8Decode = encoder.GetDecoder();  
            byte[] todecodeByte = Convert.FromBase64String(sData);  
            int charCount = utf8Decode.GetCharCount(todecodeByte, 0, todecodeByte.Length);  
            char[] decodedChar = new char[charCount];  
            utf8Decode.GetChars(todecodeByte, 0, todecodeByte.Length, decodedChar, 0);  
            string result = new String(decodedChar);  
            return result;  
        }  
        catch (Exception ex)  
        {  
            throw new Exception("Error in base64Decode" + ex.Message);  
        }  
    }  
}   
然後我們最重要得Controller
public ActionResult Registration()  
{  
    return View();  
}  
[ValidateAntiForgeryToken]  
[HttpPost]  
public ActionResult Registration(User objNewUser)  
{  
    try  
    {  
        using(var context = new CmsDbContext())  
        {  
            var chkUser = (from s in context.ObjRegisterUser where s.UserName == objNewUser.UserName || s.EmailId == objNewUser.EmailId select s).FirstOrDefault();  
            if (chkUser == null)  
            {  
                var keyNew = Helper.GeneratePassword(10);  
                var password = Helper.EncodePassword(objNewUser.Password, keyNew);  
                objNewUser.Password = password;  
                objNewUser.CreateDate = DateTime.Now;  
                objNewUser.ModifyDate = DateTime.Now;  
                objNewUser.VCode = keyNew;  
                context.ObjRegisterUser.Add(objNewUser);  
                context.SaveChanges();  
                ModelState.Clear();  
                return RedirectToAction("LogIn", "Login");  
            }  
            ViewBag.ErrorMessage = "User Allredy Exixts!!!!!!!!!!";  
            return View();  
        }  
    }  
    catch (Exception e)  
    {  
        ViewBag.ErrorMessage = "Some exception occured" + e;  
        return View();  
    }  
} 
最後我們登入需要解密去對應我們DB的hash
public ActionResult Login()  
{  
    return View();  
}  
[ValidateAntiForgeryToken]  
[HttpPost]  
public ActionResult LogIn(string userName, string password)  
{  
    try  
    {  
        using(var context = new CmsDbContext())  
        {  
            var getUser = (from s in context.ObjRegisterUser where s.UserName == userName || s.EmailId == userName select s).FirstOrDefault();  
            if (getUser != null)  
            {  
                var hashCode = getUser.VCode;  
                //Password Hasing Process Call Helper Class Method    
                var encodingPasswordString = Helper.EncodePassword(password, hashCode);  
                //Check Login Detail User Name Or Password    
                var query = (from s in context.ObjRegisterUser where(s.UserName == userName || s.EmailId == userName) && s.Password.Equals(encodingPasswordString) select s).FirstOrDefault();  
                if (query != null)  
                {  
                    //RedirectToAction("Details/" + id.ToString(), "FullTimeEmployees");    
                    //return View("../Admin/Registration"); url not change in browser    
                    return RedirectToAction("Index", "Admin");  
                }  
                ViewBag.ErrorMessage = "Invallid User Name or Password";  
                return View();  
            }  
            ViewBag.ErrorMessage = "Invallid User Name or Password";  
            return View();  
        }  
    }  
    catch (Exception e)  
    {  
        ViewBag.ErrorMessage = " Error!!! contact cms@info.in";  
        return View();  
    }  
}