Day 5 - Using Argon2 for Password Verifying with ASP.NET Web Forms C# 使用 Argon2 验证密码

=x= 🌵 Sign In page 後台登入密码验证。


验证流程介绍 :

https://ithelp.ithome.com.tw/upload/images/20210918/20139487OimBuuOXjf.jpg

📌 使用者於登入页面输入帐号及密码送出後,後端会依取得帐号连进资料库比对,如果帐号不存在,就送出帐号不存在的说明,如果帐号存在,就取出该帐号 "密码加盐加密结果" + "盐的内容",再将取出的 "盐的内容" 与登入页输入的密码又一次进行 hash 杂凑加密,最後将加密後的结果比对资料库取出的 "密码加盐加密结果",如果比对不相同,就送出密码错误的说明,如果比对相同就进行跳转页面,登入成功。



Sign In page - Argon2 验证实作 :

🧠 密码如果验证成功会一并发出 FormsAuthenticationTicket 验证票,验证票可用来进行权限的识别导引,以及再後台操作时保持登入状态,以下程序码会一并呈现使用方式。

👀 验证与授权必读好文1 : 简介 ASP.NET 表单验证 (FormsAuthentication) 的运作方式

👀 验证与授权必读好文2 : [ASP.NET] Forms 验证与授权

1. 在 Web.config 的增加使用表单验证设定如下

<configuration>
  <system.web>
        <authentication mode="Forms"></authentication>
  </system.web>
</configuration>


2. 於登入页後置程序码加入验证功能

// Argon2 验证加密密码
// Hash 处理加盐的密码功能
private byte[] HashPassword(string password, byte[] salt)
{
    var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password));

    //底下这些数字会影响运算时间,而且验证时要用一样的值
    argon2.Salt = salt;
    argon2.DegreeOfParallelism = 8; // 4 核心就设成 8
    argon2.Iterations = 4; //迭代运算次数
    argon2.MemorySize = 1024 * 1024; // 1 GB

    return argon2.GetBytes(16);
}
//验证
private bool VerifyHash(string password, byte[] salt, byte[] hash)
{
    var newHash = HashPassword(password, salt);
    return hash.SequenceEqual(newHash); // LINEQ
}


3. 设定 Cookie 验证票方法

//设定验证票
private void SetAuthenTicket(string userData, string userId)
{
    //宣告一个验证票 //需额外引入 using System.Web.Security;
    FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, userId, DateTime.Now, DateTime.Now.AddHours(3), false, userData);
    //加密验证票
    string encryptedTicket = FormsAuthentication.Encrypt(ticket);
    //建立 Cookie
    HttpCookie authenticationCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
    //将 Cookie 写入回应
    Response.Cookies.Add(authenticationCookie);
}
  • 🌵 验证票的AddHours要配合使用情境设定,使用者切换页面时可以保持登入状态。

4. 在登入页送出按钮 Click 事件加入以下程序码

protected void Button1_Click(object sender, EventArgs e)
{
    string password = TextBox2.Text;

    // 1.连线资料库
    SqlConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["TayanaYachtConnectionString"].ConnectionString);
    // 2.sql语法 (@参数化避免隐码攻击)
    string sql = "SELECT * FROM managerData WHERE account = @account";
    // 3.创建 command 物件
    SqlCommand command = new SqlCommand(sql, connection);
    // 4.放入参数化资料
    command.Parameters.AddWithValue("@account", TextBox1.Text);
    // 5.资料库用 Adapter 执行指令
    SqlDataAdapter dataAdapter = new SqlDataAdapter(command);
    // 6.建立一个空的 Table
    DataTable dataTable = new DataTable();
    // 7.将资料放入 Table
    dataAdapter.Fill(dataTable);
    // 登入流程管理 (Cookie)
    if (dataTable.Rows.Count > 0) {
        // SQL 有找到资料时执行

        //将字串转回 byte
        byte[] hash = Convert.FromBase64String(dataTable.Rows[0]["password"].ToString());
        byte[] salt = Convert.FromBase64String(dataTable.Rows[0]["salt"].ToString());
        //验证密码
        bool success = VerifyHash(password, salt, hash);

        if (success) {
            //宣告验证票要夹带的资料 (用;区隔)
            string userData = dataTable.Rows[0]["maxPower"].ToString() + ";" + dataTable.Rows[0]["account"].ToString() + ";" + dataTable.Rows[0]["name"].ToString() + ";" + dataTable.Rows[0]["email"].ToString();
            //设定验证票(夹带资料,cookie 命名) // 需额外引入using System.Web.Configuration;
            SetAuthenTicket(userData, TextBox1.Text);
            //导页至权限分流页
            Response.Redirect("CheckAccount.ashx");
        }
        else {
            //资料库里找不到相同资料时,表示密码有误!
            Label4.Text = "password error, login failed!";
            Label4.Visible = true;
            connection.Close();
            return;
        }
    }
    else {
        //资料库里找不到相同资料时,表示帐号有误!
        Label4.Text = "Account error, login failed!";
        Label4.Visible = true;
        //终止程序
        //Response.End(); //会清空页面
        return;
    }
        connection.Close();
}
  • 🌵 权限分流页 "CheckAccount.ashx" 可先暂时替换成任一页面进行测试。

5. 渲染网页并输入帐密验证确认会不会实现上述功能,完成~



密码验证功能总结 :

📢 因为使用 Argon2 验证会增加运算时间,跳转页面会有延迟,建议在 .aspx 页面用 JavaScript 监听按钮加入验证中的 CSS 盖版动画,另外登入页可增加一按钮当成返回前台首页的功能,权限分流页使用 .ashx (泛型处理常式)制作,在後续页面会一并介绍相关设定及功能。

补充加胡椒 :

🧂 要使密码更安全的作法,其实还要加"安全盐"或称作"胡椒",因为盐会和加密後的密码一起放在资料库,如果资料库被攻击就可能会被破解,"胡椒"需要另外存放来达到提升安全性的效果,或是把它写在程序里,这样至少也是放在不同地方。

👀 胡椒的使用方式 : How to apply a pepper correctly to bcrypt?

👀 胡椒的维基百科 : Pepper (cryptography)

  • 明日将介绍如何制作权限分流及清除页面快取功能。

<<:  [Day14] Vite 出小蜜蜂~ Game Logic - Sequential Movement!

>>:  Day5 Python基础语法三

[Day20] placeholder for d10

写在前面 placeholder for d10 placeholder for d10 place...

Day13:[解题技巧]Two pointers -  双指针

双指针算是一个解题蛮常用的小技巧,双指针指的是用两个指针对整个资料做遍历,而双指针又依照移动的方向...

D31 - 「来互相伤害啊!」:无聊我要见到血流成河

上竞技场就是要决斗阿,不然要干嘛。 来让人物发射武器!血流成河吧! 首先来回顾一下 D27 武器规划...

Blazor 开发入门系列

2022 新的一年降临了,回首望望过去,原本用 Blog 方式写下技术文, 後来尝试用 youtub...

Day10 日志管理工具 - Logrotate (Linux)

随着时间资料累积久都会有占空间与查询不易的问题,在Mysql的日志中常用的error log 和 s...