C# 如何实现密码重置?
我正在ASP.NET中开发一个应用程序,特别想知道如果我想自己开发,如何实现密码重置功能 具体而言,我有以下问题:C# 如何实现密码重置?,c#,asp.net,asp.net-mvc,security,C#,Asp.net,Asp.net Mvc,Security,我正在ASP.NET中开发一个应用程序,特别想知道如果我想自己开发,如何实现密码重置功能 具体而言,我有以下问题: 生成难以破解的唯一ID的好方法是什么 应该有一个计时器连接到它吗?如果是,需要多长时间 我应该记录IP地址吗?这有关系吗 在“密码重置”屏幕下,我应该询问哪些信息?只有电子邮件地址吗?或者电子邮件地址加上一些他们“知道”的信息?(最喜欢的球队、小狗的名字等) 还有其他需要注意的事项吗 NB:完全进行技术实施。事实上,公认的答案掩盖了血淋淋的细节。我希望这个问题和随后的回答会深入
- 生成难以破解的唯一ID的好方法是什么李>
- 应该有一个计时器连接到它吗?如果是,需要多长时间
- 我应该记录IP地址吗?这有关系吗
- 在“密码重置”屏幕下,我应该询问哪些信息?只有电子邮件地址吗?或者电子邮件地址加上一些他们“知道”的信息?(最喜欢的球队、小狗的名字等)
编辑:如果您能回答如何在SQL Server或任何ASP.NET MVC链接中对此类表格进行建模和处理,您将不胜感激。您可以通过链接向用户发送电子邮件。此链接将包含一些难以猜测的字符串(如GUID)。在服务器端,您还将存储发送给用户的相同字符串。现在,当用户按下链接时,您可以在数据库条目中找到一个相同的秘密字符串并重置其密码。发送到记录电子邮件地址的GUID对于大多数普通应用程序来说已经足够了,超时效果更好
毕竟,如果用户邮箱已被破坏(即黑客拥有电子邮件地址的登录/密码),您对此无能为力。首先,我们需要了解您对该用户的了解。显然,您有一个用户名和一个旧密码。你还知道什么?你有电子邮件地址吗?你有关于用户最喜欢的花的数据吗 假设您有用户名、密码和工作电子邮件地址,则需要向用户表(假设它是数据库表)添加两个字段:一个名为new_passwd_expire的日期和一个字符串new_passwd_id 假设您拥有用户的电子邮件地址,当有人请求重置密码时,您将按如下方式更新用户表:
new_passwd_expire = now() + some number of days
new_passwd_id = some random string of characters (see below)
接下来,您将向该地址的用户发送电子邮件:
亲爱的某某
有人已在请求用户帐户的新密码。如果您确实请求重置此密码,请点击以下链接:
并在表格中输入以下内容:
如果您没有请求密码重置,您可以忽略此电子邮件
谢谢,雅达雅达
现在,编写你的脚本。lang:这个脚本需要一个表单。如果var更新通过URL传递,那么表单只要求用户的用户名和电子邮件地址。如果更新未通过,它将要求输入用户名、电子邮件地址和电子邮件中发送的id代码。您还需要一个新密码(当然是两次)
要验证用户的新密码,请验证用户名、电子邮件地址和id代码是否匹配,请求是否已过期,以及两个新密码是否匹配。如果成功,将用户密码更改为新密码,并从用户表中清除密码重置字段。还要确保用户注销/清除任何与登录相关的cookie,并将用户重定向到登录页面
从本质上讲,新的\u passwd \u id字段是一个仅在密码重置页面上有效的密码
一个潜在的改进:你可以从电子邮件中删除。“有人已请求在此电子邮件地址重置帐户的密码…”因此,如果电子邮件被截获,只有用户知道用户名。我不是这样开始的,因为如果有人攻击这个帐户,他们已经知道用户名了。这增加了隐蔽性,阻止了中间人的机会攻击,以防有人恶意拦截电子邮件
关于你的问题:
生成随机字符串:不需要非常随机。任何GUID生成器甚至md5(concat(salt,current_timestamp())都足够了,其中salt是用户记录上的某个东西,比如创建了时间戳帐户。它必须是用户看不到的东西
定时器:是的,你需要这个只是为了保持你的数据库正常。实际上不超过一周是必要的,但至少两天,因为你永远不知道电子邮件延迟会持续多久
IP地址:由于电子邮件可能延迟数天,IP地址仅用于日志记录,不用于验证。如果你想记录它,就这样做,否则你就不需要它了
重置屏幕:见上文
希望这能涵盖它。祝你好运。1)要生成唯一id,可以使用安全哈希算法。
2) 定时器连接?你是说重置pwd链接过期了吗?
是的,你可以有一套有效期
3) 您可以要求验证emailId以外的其他信息。。
比如出生日期或者一些安全问题
4) 您还可以生成随机字符,并要求输入该字符以及
要求为了确保密码请求不会被某些间谍软件或诸如此类的东西自动执行。编辑2012/05/22:作为这个流行答案的后续,我本人在这个过程中不再使用GUID。与其他流行答案一样,我现在使用自己的哈希算法生成要发送到URL中的密钥。这也有缩短的优点。查看System.Security.Cryptography以生成它们,我通常也使用SALT 首先,不要立即重置用户的密码。 首先,当用户请求密码时,不要立即重置密码。这是一种安全漏洞,因为有人可能会猜测电子邮件地址(即您在公司的电子邮件地址)并根据突发奇想重置密码。目前的最佳实践通常包括“确认”li
<add key="AllowedHosts" value="example.com,example2.com" />
[Route("RequestResetPasswordToken/{email}/")]
[HttpGet]
[AllowAnonymous]
public async Task<IHttpActionResult> GetResetPasswordToken([FromUri]string email)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var user = await UserManager.FindByEmailAsync(email);
if (user == null)
{
Logger.Warn("Password reset token requested for non existing email");
// Don't reveal that the user does not exist
return NoContent();
}
//Prevent Host Header Attack -> Password Reset Poisoning.
//If the IIS has a binding to accept connections on 80/443 the host parameter can be changed.
//See https://security.stackexchange.com/a/170759/67046
if (!ConfigurationManager.AppSettings["AllowedHosts"].Split(',').Contains(Request.RequestUri.Host)) {
Logger.Warn($"Non allowed host detected for password reset {Request.RequestUri.Scheme}://{Request.Headers.Host}");
return BadRequest();
}
Logger.Info("Creating password reset token for user id {0}", user.Id);
var host = $"{Request.RequestUri.Scheme}://{Request.Headers.Host}";
var token = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
var callbackUrl = $"{host}/resetPassword/{HttpContext.Current.Server.UrlEncode(user.Email)}/{HttpContext.Current.Server.UrlEncode(token)}";
var subject = "Client - Password reset.";
var body = "<html><body>" +
"<h2>Password reset</h2>" +
$"<p>Hi {user.FullName}, <a href=\"{callbackUrl}\"> please click this link to reset your password </a></p>" +
"</body></html>";
var message = new IdentityMessage
{
Body = body,
Destination = user.Email,
Subject = subject
};
await UserManager.EmailService.SendAsync(message);
return NoContent();
}
[HttpPost]
[Route("ResetPassword/")]
[AllowAnonymous]
public async Task<IHttpActionResult> ResetPasswordAsync(ResetPasswordRequestModel model)
{
if (!ModelState.IsValid)
return NoContent();
var user = await UserManager.FindByEmailAsync(model.Email);
if (user == null)
{
Logger.Warn("Reset password request for non existing email");
return NoContent();
}
if (!await UserManager.UserTokenProvider.ValidateAsync("ResetPassword", model.Token, UserManager, user))
{
Logger.Warn("Reset password requested with wrong token");
return NoContent();
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Token, model.NewPassword);
if (result.Succeeded)
{
Logger.Info("Creating password reset token for user id {0}", user.Id);
const string subject = "Client - Password reset success.";
var body = "<html><body>" +
"<h1>Your password for Client was reset</h1>" +
$"<p>Hi {user.FullName}!</p>" +
"<p>Your password for Client was reset. Please inform us if you did not request this change.</p>" +
"</body></html>";
var message = new IdentityMessage
{
Body = body,
Destination = user.Email,
Subject = subject
};
await UserManager.EmailService.SendAsync(message);
}
return NoContent();
}
public class ResetPasswordRequestModel
{
[Required]
[Display(Name = "Token")]
public string Token { get; set; }
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 10)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}