如何实施密码重置链接

时间:2015-03-12 14:00:37

标签: c# sql-server visual-studio-2010 password-recovery

我目前有一个系统,如果用户忘记了密码,他们可以通过点击忘记密码链接重置密码。他们将被带到他们输入用户名/电子邮件的页面,然后会向用户发送一封电子邮件,我想知道如何在电子邮件中实现密码重置链接,以便用户点击链接后/她被带到一个页面,允许他们重置密码。

这是我控制器中的代码

public ActionResult ForgotPassword()
        {
           //verify user id

            string UserId = Request.Params ["txtUserName"];
            string msg = "";
            if (UserId == null) 
            {
                msg = "You Have Entered An Invalid UserId - Try Again";
                ViewData["ForgotPassword"] = msg;
                return View("ForgotPassword");
            }

            SqlConnection lsql = null;
            lsql = DBFactory.GetInstance().getMyConnection();

            String sqlstring = "SELECT * from dbo.[USERS] where USERID = '" + UserId.ToString() + "'";
            SqlCommand myCommand = new SqlCommand(sqlstring, lsql);
            lsql.Open();
            Boolean validUser;         
            using (SqlDataReader myReader = myCommand.ExecuteReader())
            {

                validUser = false;
                while (myReader.Read())
                {
                    validUser = true;

                }
                myReader.Close();
            }
            myCommand.Dispose();  


            if (!validUser) 
                  {
                msg = "You Have Entered An Invalid UserId - Try Again";
                ViewData["ForgotPassword"] = msg;
                lsql.Close();
                return View("ForgotPassword");
            }

            //run store procedure


            using (lsql)
            {
                SqlCommand cmd = new SqlCommand("Stock_Check_Test.dbo.RESET_PASSWORD", lsql);
                cmd.CommandType = CommandType.StoredProcedure;

                SqlParameter paramUsername = new SqlParameter("@var1", UserId);

                cmd.Parameters.Add(paramUsername);


                SqlDataReader rdr = cmd.ExecuteReader();
                while (rdr.Read())
                {
                    if (Convert.ToInt32(rdr["RC"]) == 99)
                    {
                        msg = "Unable to update password at this time";
                        ViewData["ForgotPassword"] = msg;
                        lsql.Close();
                        return View("ForgotPassword");  

                    }
                }
            }


            msg = "new password sent";
            ViewData["ForgotPassword"] = msg;
            lsql.Close();
            return View("ForgotPassword");
        }

这是我当前的存储过程,它向用户发送电子邮件

ALTER PROCEDURE [dbo].[A_SEND_MAIL]
    @var1 varchar (200), -- userid
    @var2 varchar (200) -- email address
AS
BEGIN
declare @bodytext varchar(200);
set @bodytext = 'Password Reset for user: ' +@var1 + ' @' + cast (getDate() as varchar) + ' ' ;
EXEC msdb.dbo.sp_send_dbmail 
@profile_name='Test',
@recipients=@var2,
@subject='Password Reset',
@body=@bodytext
END 

GO

2 个答案:

答案 0 :(得分:24)

创建一个具有类似

结构的表
create table ResetTickets(
    username varchar(200),
    tokenHash varbinary(16),
    expirationDate datetime,
    tokenUsed bit)

然后在您的代码中,当用户单击重置密码按钮时,您将生成一个随机令牌,然后在该表中放入一个条目,其散列值为token,过期日期类似于{{1}并将该标记值附加到您通过电子邮件发送给用户以获取密码重置页面的URL。

DATEADD(day, 1, GETDATE())

在密码重置页面上,您获取传入的用户名和令牌,再次对令牌进行散列,然后将其与www.example.com/passwordReset?username=Karan&token=ZB71yObR 表进行比较,如果到期日期尚未过期且令牌尚未使用然后将用户带到一个页面,让他们输入一个新密码。

需要注意的事项

  1. 请务必使令牌失效,不要让两年前的电子邮件重置密码。
  2. 确保将令牌标记为已使用,不要让计算机的其他用户使用浏览器的历史记录重置其他用户密码。
  3. 确保您安全地生成随机令牌。不要使用ResetTickets并使用它来生成令牌,同时重置的​​两个用户将得到相同的令牌(我可以同时重置我的密码和密码然后使用我的令牌重置您的帐户)。而是使用静态RNGCryptoServiceProvider并使用Rand方法,该类是线程安全的,因此您不必担心使用相同实例的两个线程。
  4. 请务必parameterize your queries在当前代码中,如果我输入了用户ID GetBytes,则会删除数据库中的所有用户。有关如何解决此问题的详细信息,请参阅链接的SO帖子。
  5. 请确保您对令牌进行哈希处理,您的'; delete dbo.[USERS] --页面只接受未散列的版本,并且您永远不会将未散列的版本存储在任何地方(包括向用户发送外向邮件的电子邮件日志)。这可以防止对数据库具有读访问权限的攻击者为其他用户创建令牌,读取电子邮件中发送的值,然后自己发送相同的值(也许可以访问可以执行更多操作的管理员用户而不只是读取值。)。

答案 1 :(得分:0)

这里有两种使用HMAC或JWT的替代方法(我认为它们提供了更好,更安全的电子邮件URL)