“新密码无效”时WebSecurity.ChangePassword何时失败?

时间:2013-05-14 16:03:12

标签: asp.net-mvc-4 security simplemembership

这是在MVC 4中支持的默认AccountController中更改密码的代码:

// ChangePassword will throw an exception rather 
//than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
    string userName = User.Identity.Name;    
    changePasswordSucceeded = WebSecurity.ChangePassword(userName, 
                                                         model.OldPassword,
                                                         model.NewPassword);
}
catch (Exception)
{
    changePasswordSucceeded = false;
}

if (changePasswordSucceeded)
{
    return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
}
else
{
    ModelState.AddModelError("",
    "The current password is incorrect or the new password is invalid.");
}

我的问题是该消息不清楚。如果当前密码不正确那么没关系,但如果新密码无效,我想向用户提供更好的消息,告诉他们出了什么问题,我想更好地理解“失败情景”,以便我可以定制消息。

文档here是关于例外的具体信息,我认为不应该在行动中吞下这些例外并将其报告为无效密码。

那么为什么这里有“新密码无效”的可能性呢?如果我在我的应用程序中仅使用没有OAuth的SimpleMembershipProvider,我可以将其删除吗?

编辑:NB此数据注释也出现在“RegisterModel”类中,因此此处也有密码有效性检查

[StringLength(100, 
ErrorMessage = "The {0} must be at least {2} characters long.", 
MinimumLength = 6)]

1 个答案:

答案 0 :(得分:5)

摘要

这是一个很长的答案,所以我把它编辑成了几个部分。与像rook这样的用户相比,我不是安全专家,甚至他说不相信他的安全答案,或其他任何人。总是自己测试和理解它。任何阅读此内容的网络开发者都应该阅读the OWASP top ten web security flaws,使用Trustworthy Computing等在线指导,当然还有Mr. Bruce Schneier。对于我们任何人来说,安全性都很复杂且太大,因此请尽可能使用根据最佳实践进行工作的框架。最重要的是,全部保持in perspective

有关这些摘要答案的更多详细信息,请参见下文:

  1. 向用户发送消息故意含糊不清,可以认为这是出于安全原因(见下文)
  2. 参考。 "失败场景",它们主要是由于无效输入应该已经被模型验证,不正确的当前密码或边缘情况例外捕获
  3. 由于原始开发人员是懒惰的,或者因为他们认为所有案例都应该有一条返回消息(见上文1),所以例外被吞并并呈现为一个例外。
  4. "新密码无效"只要您的属性匹配,或者比ChangePassword 中的长度限制更具限制性,您可能无法突出显示的代码。但是,见上文(1)。
  5. 我可以将其删除吗?见上文(1)。
  6. 好的,请参阅下面的讨论。
  7. 原始答案 - 概念攻击向量

    从安全角度考虑:

    • 我喜欢坐在酒吧/咖啡店桌子上的手机/平板电脑/笔记本电脑(或家庭/办公室的台式电脑)
    • 我想我可能知道你的密码
    • 我转到"更改密码"并输入我认为您的密码不是
    • 我故意在
    • 中添加1个字符的新密码

    机器告诉我

      

    "当前密码不正确"

    现在我再次尝试使用我认为您当前的密码:

      

    "新密码无效"

    我现在知道你的密码,但你不知道我做了什么,因为我没有改变任何东西。

    脚注:我得到第一组逻辑错误,删除它,编辑并重新发布,但是嘿 - I'm not Bruce

    类似的攻击

    这种攻击有很多组合,但它就像从糟糕的登录系统中收集电子邮件一样。想想如果我在尝试“恢复密码”时收到两条不同的消息会发生什么?使用电子邮件地址:

    • 电子邮件已发送到您的地址(对于您数据库中的电子邮件)
    • 找不到您的电子邮件(对于明显虚假的电子邮件地址)

    然后,我可以轻松找到您网站上的有效会员电子邮件,以便针对该用户进行有针对性的网上诱骗攻击,或者只是为垃圾邮件构建有效的电子邮件地址列表。

    可能的解决方案

    在两种情况下,我们更改密码的用户友好且同样安全的错误消息是:

      

    "当前密码不正确或新密码无效。请记住,您的密码区分大小写,新密码必须包含[64 letters/5 numbers/4 characters / 3个希腊神的名字]"

    为了保持理智,请将其保留在您的应用程序使用环境中,但请记住,您有责任使用更大的生态系统,users share passwords between websites,无论您是否喜欢。

    ChangePassword何时返回false?

    关于ChangePassword何时返回false的问题部分:

    基本上WebSecurity.ChangePassword

    总之,如果符合以下情况,它会返回false

    1. 任何参数都会失败,空或长度检查;或
    2. 数据库连接失败;或
    3. 数据库中不再存在UserId;或
    4. 当前密码不正确;或
    5. (显然,如果发生任何其他异常,它将设置changePasswordSucceeded = false。)
    6. 在致电newPassword
    7. 时,没有 ChangePassword有效性检查

      所以从理论上讲,如果你的验证属性正确,忽略边缘情况,如果我们有边缘情况(用户已被删除,在Get和Post之间,则只能return false,或者我们无法访问数据库,或者当前密码无效)。

      即使在攻击者绕过浏览器中的UI(他们将会绕过),因为Action本身在模型上调用IsValid,这一切都是正确的。

      客户端消息

      这里有一个严肃的免责声明:我不会在安全上花费我的生命。我遵循安全设计原则(例如,我跟上the OWASP top ten project),并相信我对最重要的安全原则有很好的认识。因此,我可能有一些错误。

      如果客户端"密码要求"验证不存在 - 会发生什么?用户必须等待Post返回才能找出发生的事情。

      如果客户端"密码要求"验证那里,它会削弱我们的界面吗?我不相信。:

      • 服务器仍然执行要求和当前密码验证
      • 服务器不区分这两种故障情况(实际上是这样,见下文)
      • 因此,服务器只会告诉您当前密码是否有效,以及密码是否发生变化。
      • 希望网站也是:
        • 防止更改回旧密码
        • 发送一封电子邮件通知,告知您的密码已被更改(可能等待15分钟,以防止攻击者将您的手机中的电子邮件从打开的电子邮件中删除,并且我们的网站在它,我们的电子邮件也开放了)

      MVC并不是AFAIK做这些事情中的任何一个,所以这就是它开始崩溃的地方。

      它也因为操作中的Model.IsValid测试而从服务器返回不同的错误和消息而分崩离析,该测试强制执行新的密码验证以及针对此失败的不同错误消息。因此,在其当前实现中,单个错误消息方法存在缺陷

      摘要

      那么我会改变这一切的方式吗?希望我及时改进其他部分,除了将返回消息更改为更长一些且信息更丰富之外,我可能不会这样做。

      这是个人意见,可以反过来论证


      细节:

      • 致电Membership.GetUser(userName, true).ChangePassword(...)。如果是ArgumentException see docs,它将返回false
      • 然后在实际提供商处调用ChangePassword(例如SimpleMembershipProvider.ChangePassword),如果失败则返回false

      实际的提供程序实现变得复杂。例如,对于SimpleMembershipProvider

      • 它可能会将呼叫转移到"之前的提供商"如果还没有初始化。
      • 否则,如果密码/用户名为空,空,太长等,它可能会抛出ArgumentExceptions(冒泡到catch中的return falseMembershipUser.ChangePassword)。
      • 如果没有发生那么它会发生
        • 尝试从数据库中获取UserId(如果失败则返回false),
        • 检查当前密码是否正确(如果失败则返回false),
        • 最后使用UPDATE查询更新密码,为您设置PasswordChangedDate
      • 然后更新MembershipUser
      • 上的内部数据