概括
这是一个很长的答案,所以我将它编辑成部分。与像rook这样的用户相比,我不是安全专家,甚至他也说不要相信他的安全答案,或者其他任何人的。始终自己测试和理解它。任何阅读本文的 Web 开发人员都应该阅读OWASP 十大 Web 安全漏洞,使用在线指南,例如Trustworthy Computing,当然还有Mr. Bruce Schneier。安全性对于我们任何人来说都是复杂且太大的,因此请尽可能使用根据最佳实践完成工作的框架。最重要的是,保持所有观点。
有关这些摘要答案的更多详细信息,请参见下文:
- 给用户的消息是故意模棱两可的,可以说这是出于安全原因(见下文)
- 参考。“失败场景”,它们主要是由于模型验证应该已经捕获的无效输入、不正确的当前密码或边缘情况异常
- 异常被吞并并呈现为一个,要么是因为原始开发人员很懒,要么是因为他们认为所有情况都应该有一个返回消息(参见上面的 1)
- 只要您的属性匹配,或者比 ChangePassword 中的长度限制更严格,您突出显示的代码就不可能出现“新密码无效” 。但是,参见上面的(1)。
- 我可以删除它吗?见上文 (1)。
- 好点子,见下文讨论。
原始答案 - 概念攻击向量
从安全角度考虑:
- 我漫步到您坐在酒吧/咖啡店桌子上的手机/平板电脑/笔记本电脑(或家庭/办公室中的台式电脑)
- 我想我可能知道你的密码
- 我去“更改密码”并输入我认为您的密码不是
- 我故意放了一个 1 个字符的新密码
机器告诉我
“当前密码不正确”
现在我再次尝试使用我认为您当前的密码是:
“新密码无效”
我现在知道你的密码,但你不知道我知道,因为我没有更改任何内容。
脚注:我把第一组逻辑弄错了,删除它,编辑它并重新发布,但是嘿-我不是布鲁斯
类似的攻击
这种攻击有很多组合,但这就像从一个糟糕的登录系统中收集电子邮件一样。想一想当我尝试使用电子邮件地址“恢复密码”时收到两条不同的消息会发生什么:
- 一封电子邮件已发送到您的地址(对于您数据库中的电子邮件)
- 找不到您的电子邮件(对于一个明显虚假的电子邮件地址)
然后我可以轻松地从您的站点找到有效的成员电子邮件,以针对该用户进行有针对性的网络钓鱼攻击,或者只是为垃圾邮件构建一个有效电子邮件地址列表。
一个可能的解决方案
在这两种情况下,对于我们的密码更改,一个更用户友好但同样安全的错误消息是:
“当前密码错误或新密码无效。记住密码区分大小写,新密码必须有[ 64个字母/5个数字/4个字符/3个希腊神名]”
为了保持清醒,请在您的应用程序使用环境中保留这一点,但请记住,您在更大的生态系统中负有责任,并且用户在网站之间共享密码,无论您是否喜欢。
ChangePassword 什么时候返回 false?
关于 ChangePassword 何时返回 false 的问题部分:
本质上WebSecurity.ChangePassword
:
总之,它在以下情况下返回false
:
- 任何参数都无法通过 null、空或长度检查;或者
- 数据库连接失败;或者
UserId
不再存在于数据库中;或者
- 当前密码不正确;或者
- (显然,
changePasswordSucceeded = false
如果发生任何其他异常,它将设置)。
- 调用中没有
newPassword
有效性检查ChangePassword
因此,理论上,如果您正确设置了验证属性,并且忽略了边缘情况,那么只有return false
当我们有边缘情况(用户已被删除,在 Get 和 Post 之间,或者我们无法访问数据库时) ,或当前密码无效)。
即使攻击者绕过浏览器中的 UI(他们会这样做),因为 Action 本身IsValid
在模型上调用,这一切都是正确的。
客户端消息
这里有一个严肃的免责声明:我不会把我的一生都花在安全上。我遵循安全设计原则(例如,我关注OWASP 十大项目),并且相信我对最重要的安全原则有很好的认识。因此,我可能弄错了其中的一些逻辑。
如果客户端“密码要求”验证不存在 - 会发生什么?用户必须等待 Post 返回才能了解发生了什么。
如果存在客户端“密码要求”验证,是否会削弱我们的界面?我不相信。:
- 服务器仍然执行要求和当前密码验证
- 服务器不区分两种故障情况(实际上是这样,见下文)
- 因此,服务器只会告诉您当前密码是否有效,以及密码何时更改。
- 希望该网站也:
- 防止更改回旧密码
- 发送一封电子邮件通知您的密码已更改(可能等待 15 分钟,以防止攻击者将其从电子邮件中删除我们的电子邮件也打开了)
AFAIK,MVC 不会做这些事情中的任何一个,所以这就是它开始崩溃的地方。
它也分崩离析,因为由于Model.IsValid
操作内部的测试,我们从服务器返回了不同的错误和消息,这会强制执行新密码验证和针对此失败的不同错误消息。因此,在其当前的实现中,单一错误消息方法是有缺陷的。
概括
那么我会改变这一切的方式吗?希望我能及时改进其他部分,我可能不会,除了将返回消息更改为更长一些和更多信息。
这是个人观点,可以换个说法
细节:
实际的提供者实现变得复杂。例如SimpleMembershipProvider
,
- 如果尚未初始化,它可能会将调用传递给“先前的提供者”。
- 否则,如果密码/用户名为空、null、太长等,它可能会抛出
ArgumentExceptions
(冒泡到catch
and return false
in )。MembershipUser.ChangePassword
- 如果这没有发生,那么它将
- 尝试
UserId
从数据库中获取(如果失败则返回 false),
- 检查当前密码是否正确(如果失败则返回 false),
- 最后使用 UPDATE 查询更新密码,
PasswordChangedDate
在它运行时为您设置
- 然后它更新上的内部数据
MembershipUser