3

这是在 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.");
}

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

此处的文档专门针对例外情况,我认为不应在操作中将这些内容吞下并报告为无效密码。

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

编辑:注意这个数据注释也出现在“RegisterModel”类中,所以这里也有密码有效性检查

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

1 回答 1

5

概括

这是一个很长的答案,所以我将它编辑成部分。与像rook这样的用户相比,我不是安全专家,甚至他也说不要相信他的安全答案,或者其他任何人的。始终自己测试和理解它。任何阅读本文的 Web 开发人员都应该阅读OWASP 十大 Web 安全漏洞,使用在线指南,例如Trustworthy Computing,当然还有Mr. Bruce Schneier。安全性对于我们任何人来说都是复杂且太大的,因此请尽可能使用根据最佳实践完成工作的框架。最重要的是,保持所有观点

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

  1. 给用户的消息是故意模棱两可的,可以说这是出于安全原因(见下文)
  2. 参考。“失败场景”,它们主要是由于模型验证应该已经捕获的无效输入、不正确的当前密码或边缘情况异常
  3. 异常被吞并并呈现为一个,要么是因为原始开发人员很懒,要么是因为他们认为所有情况都应该有一个返回消息(参见上面的 1)
  4. 只要您的属性匹配,或者比 ChangePassword 中的长度限制更严格,您突出显示的代码就不可能出现“新密码无效” 。但是,参见上面的(1)。
  5. 我可以删除它吗?见上文 (1)。
  6. 好点子,见下文讨论。

原始答案 - 概念攻击向量

从安全角度考虑:

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

机器告诉我

“当前密码不正确”

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

“新密码无效”

我现在知道你的密码,但你不知道我知道,因为我没有更改任何内容。

脚注:我把第一组逻辑弄错了,删除它,编辑它并重新发布,但是嘿-我不是布鲁斯

类似的攻击

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

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

然后我可以轻松地从您的站点找到有效的成员电子邮件,以针对该用户进行有针对性的网络钓鱼攻击,或者只是为垃圾邮件构建一个有效电子邮件地址列表。

一个可能的解决方案

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

“当前密码错误或新密码无效。记住密码区分大小写,新密码必须有[ 64个字母/5个数字/4个字符/3个希腊神名]”

为了保持清醒,请在您的应用程序使用环境中保留这一点,但请记住,您在更大的生态系统中负有责任,并且用户在网站之间共享密码,无论您是否喜欢。

ChangePassword 什么时候返回 false?

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

本质上WebSecurity.ChangePassword

总之,它在以下情况下返回false

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

因此,理论上,如果您正确设置了验证属性,并且忽略了边缘情况,那么只有return false当我们有边缘情况(用户已被删除,在 Get 和 Post 之间,或者我们无法访问数据库时) ,或当前密码无效)。

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

客户端消息

这里有一个严肃的免责声明:我不会把我的一生都花在安全上。我遵循安全设计原则(例如,我关注OWASP 十大项目),并且相信我对最重要的安全原则有很好的认识。因此,我可能弄错了其中的一些逻辑。

如果客户端“密码要求”验证不存在 - 会发生什么?用户必须等待 Post 返回才能了解发生了什么。

如果存在客户端“密码要求”验证,是否会削弱我们的界面?我不相信。:

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

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

它也分崩离析,因为由于Model.IsValid操作内部的测试,我们从服务器返回了不同的错误和消息,这会强制执行新密码验证和针对此失败的不同错误消息。因此,在其当前的实现中,单一错误消息方法是有缺陷的

概括

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

这是个人观点,可以换个说法


细节:

  • 来电Membership.GetUser(userName, true).ChangePassword(...)。在 ArgumentException 的情况下,请参阅文档,它将返回false
  • 然后它调用ChangePassword实际的提供者(例如SimpleMembershipProvider.ChangePassword),false如果失败将返回。

实际的提供者实现变得复杂。例如SimpleMembershipProvider

  • 如果尚未初始化,它可能会将调用传递给“先前的提供者”。
  • 否则,如果密码/用户名为空、null、太长等,它可能会抛出ArgumentExceptions(冒泡到catchand return falsein )。MembershipUser.ChangePassword
  • 如果这没有发生,那么它将
    • 尝试UserId从数据库中获取(如果失败则返回 false),
    • 检查当前密码是否正确(如果失败则返回 false),
    • 最后使用 UPDATE 查询更新密码,PasswordChangedDate在它运行时为您设置
  • 然后它更新上的内部数据MembershipUser
于 2013-05-14T18:26:05.620 回答