我的一位客户不想使用2FA的任何标准选项(短信或电子邮件),我想知道其他人已经实施了什么。
我觉得只要使用用户名和密码组合,该网站就太脆弱了,即使使用最大尝试次数和超时也是如此。
将登录不确定性倍增的简单选项是在登录页面中添加一个额外的安全问题。
我的答案发布在下面
答案 0 :(得分:0)
在创建用户数据库时使用代码优先方法,我在IdentityDbContext类中添加了一组安全问题。
public DbSet<SecurityQuestion> SecurityQuestions { get; set; }
这提供了一个简单的问题列表,例如&#34;你最喜欢的食物是什么&#34;。问题应该产生合理的通用答案。问题将添加到Configuration类Seed方法
中void AddSecurityQuestion(ApplicationDbContext db, string question)
{
db.SecurityQuestions.Add(new SecurityQuestion() { Question = question });
}
一个简单的表就足够了
public class SecurityQuestion
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(128)]
[DisplayName("Question")]
public string Question { get; set; }
}
添加到Identity User类的字段。这将包含null或安全问题和答案的哈希值。为了完整性,添加了一个属性来检查是否存在哈希。用户第一次登录时,会保存哈希值。在后续登录中,检查哈希
public string SecurityQuestion { get; protected set; }
[NotMapped]
public bool HasSecurityQuestion
{
get
{
return this.SecurityQuestion != null;
}
}
Hashing使用与内部Identity方法相同的代码,并将种子和哈希存储在同一个字符串中
public static string HashSecurityQuestion(string question, string answer)
{
if (question == null)
{
throw new ArgumentNullException("Question is null");
}
if (answer == null)
{
throw new ArgumentNullException("Answer is null");
}
string questionAndAnswer = question + "_" + answer;
// random salt and hash in save result
byte[] salt;
byte[] buffer2;
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(questionAndAnswer, 0x10, 0x3e8))
{
salt = bytes.Salt;
buffer2 = bytes.GetBytes(0x20);
}
byte[] dst = new byte[0x31];
Buffer.BlockCopy(salt, 0, dst, 1, 0x10);
Buffer.BlockCopy(buffer2, 0, dst, 0x11, 0x20);
return Convert.ToBase64String(dst);
}
需要验证方法
public static bool VerifyHashedPassword(string hashedSecurityQuestion, string question, string answer)
{
if (hashedSecurityQuestion == null)
{
return false;
}
if (question == null)
{
throw new ArgumentNullException("Question is null");
}
if (answer == null)
{
throw new ArgumentNullException("Answer is null");
}
string questionAndAnswer = question + "_" + answer;
// has to retrieve salt
byte[] buffer4;
byte[] src = Convert.FromBase64String(hashedSecurityQuestion);
if ((src.Length != 0x31) || (src[0] != 0))
{
return false;
}
byte[] dst = new byte[0x10];
Buffer.BlockCopy(src, 1, dst, 0, 0x10);
byte[] buffer3 = new byte[0x20];
Buffer.BlockCopy(src, 0x11, buffer3, 0, 0x20);
using (Rfc2898DeriveBytes bytes = new Rfc2898DeriveBytes(questionAndAnswer, dst, 0x3e8))
{
buffer4 = bytes.GetBytes(0x20);
}
return buffer3.SequenceEqual(buffer4);
}
在登录过程中,还有一个额外步骤可以验证是否已检查安全问题和答案。 MVC视图显示问题的下拉列表和答案的文本框,这两个值都在视图模型中
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: true);
// if the user login is a success, check if a security question exists
if (result == SignInStatus.Success && user.HasSecurityQuestion)
{
// security question exists, so check it
if (!user.VerifySecrityQuestion(model.SecurityQuestion, model.SecurityQuestionAnswer))
{
result = SignInStatus.Failure;
}
}
答案 1 :(得分:0)
Authy / Twilio开发者传播者在这里。您还有其他一些选项可以提供额外的安全性:
强制执行&amp;鼓励使用强密码
这包括最小长度,显示密码和强度等内容。 indicator,包括人们使用密码管理器的简便方法。
我编译了有关强密码建议的更多详细信息: https://github.com/robinske/betterpasswords
TOTP
形式的一次性密码这是您在Authy或Google Authenticator等应用中看到的内容。 TOTP(基于时间的一次性密码)是标准,您可以阅读here。
Authy has APIs to implement OTPs here.
推送身份验证
这是2FA的另一种形式,允许您的用户批准&#34;或者&#34;拒绝&#34;推送通知形式的登录请求。这是2FA的最安全形式,具有无缝的用户体验you can read more about how Authy does that here.
Authy has APIs to implement push authentication here.
======
注意:安全问题很像其他密码,可以更容易用Google搜索,所以我鼓励您的客户考虑使用真正的第二个因素。