如果表单未通过验证,密码字段是不是应保留其值?
Posted
技术标签:
【中文标题】如果表单未通过验证,密码字段是不是应保留其值?【英文标题】:Should Password fields retain their values if a form does not pass validation?如果表单未通过验证,密码字段是否应保留其值? 【发布时间】:2011-09-26 07:19:04 【问题描述】:我有一个带有两个密码字段的典型注册表单。
<form>
<%= html.TextBox("Email", null) %>
<%= Html.Password("password", null) %>
<%= Html.Password("confirmPassword", null) %>
<input type='submit' />
</form>
如果表单验证失败并重新显示,文本字段将保留其值,但密码字段始终为空白。
为什么密码字段不应该保留它们的值?更重要的是,我有什么理由不应该覆盖这种行为吗?
我觉得这种行为会降低可用性,并且希望密码字段的行为方式与文本框字段相同——在存在验证错误时保留输入的值。
我正在使用 ASP.NET MVC,但这个问题更多地涉及可用性和安全性。我知道我看到的是预期行为,查看 Password(...)
方法表明它明确忽略了 ModelState
中的值。
【问题讨论】:
这不仅仅是 MVC 特有的东西。这似乎是整个网络的普遍做法。每当您通过某些注册表单但犯了一些错误时,当该表格返回给您时,您的密码就消失了。我想你确实有一个有效的问题,为什么是...... +1 非常有趣。注册失败时重新输入密码让我很恼火。 我想知道这是否是一种安全做法。我想有可能返回的带有填充密码字段的页面可能会缓存在客户端上,并且窥探本地缓存的人可能会发现密码。 @GWB:有趣,缓存。可能的答案! @Platinum Azure 好吧,好的。如果您愿意,您仍然可以只收听出站流量,不将其发送到入站不会降低收听流量的能力 【参考方案1】:我过去听到的解释是,如果用户点击提交并离开他们的计算机,其他人不应该在他们不知情的情况下出现并重新提交。
【讨论】:
对我来说似乎有些牵强。邪恶的用户仍然不会轻易得到密码,他只是可以更改密码(那他为什么要这样做?) @Oskar:如果您在验证失败甚至回发后保留(正确的)密码,那么您将面临一个很大的安全漏洞。正如尼克所说,人们可以登录并做他们想做的任何事情(网上银行等)。如果用户离开页面但下一个用户在浏览器中单击后退按钮,只要他到达您填充的 assword-field,甚至会出现此问题。 为了覆盖默认浏览器行为,您必须使用 value 属性,这意味着如果您查看源代码,您将看到密码。浏览器默认不会保留密码字段。 @Tim 不改变这种行为是一个很好的理由,因为人们可以按回并获取密码,但这不是他写的 这是一个很好的观点。如果网站执行不好,“注册失败”页面可以保留在历史记录中,然后Evil用户可以使用返回按钮+“查看源代码”查看密码。但是,在这种情况下,安全漏洞在于 浏览器 会记住提交的密码并重新发布。 Evil 用户可以使用“查看源代码”以外的方法来查看浏览器的 POST(例如 FireBug)。我相信 PRG 模式是防止浏览器重新发布密码的最佳选择。【参考方案2】:您可以将值发送回常规输入 type=password 字段。
但是,如果您使用的是 .net 输入控件,那么它将在将 html 发送回客户端之前清除 value 的内容。
原因很简单:他们想限制密码在服务器和浏览器之间来回发送的次数。这有助于限制对某些系统的暴露。(link)
现在,显然,如果您使用的是 ssl,那么这并不是什么大问题。不幸的是,那里的绝大多数站点仍然不使用 SSL,并且很乐意以明文形式来回发送数据。该字段在客户端和服务器之间传输的次数越多,有人抓住它的机会就越大,比如 FireSheep。
请记住,这并不是说倾听整个对话的人不会从第一个帖子中得到它。但是,请将其视为限制(而不是消除)攻击面的简单选项。
下一个原因是,几乎每次网站在提交后向用户显示密码字段时,都是因为验证未通过。这可能意味着用户名和/或密码不正确。考虑到密码字段仅向用户显示星号或圆点,因此没有真正的理由将其归还给他们。
鉴于您永远不想告诉用户哪些凭据失败(即:您不想说“密码无效”或“用户名无效”)并且普通用户无法确定他们是否胖手指他们的条目,恕我直言,清除两者要好得多。
除此之外,您还有一个选择。标准是空白。考虑到这是绝大多数网站的工作方式,你真的想反其道而行之吗?就个人而言,我发现即使我们不同意 UI 标准,我们也会更好地坚持这些标准。
用户已经很难使用所有不同的选项。
【讨论】:
感谢您深思熟虑的回答。但是,我不同意“限制攻击面”会增加安全性。如果你有一个 HTTP 站点,你的用户名和密码是明文发送的,将流量减少 @Scott:我同意,这没有多大帮助。然而,这就是它背后的想法,我相信这就是你所要求的...... ;) 回到注册页面,我再次相信它们都应该被清除。如果由于密码不匹配而出错,用户怎么可能知道哪个错了?他们不会,所以为了善待你的用户,你清除他们并要求他们再试一次。从可用性的角度来看,这比面对两个装满点的盒子而且不知道要清除哪个要好得多。面对这种情况,用户将清除两者并重试。 @Scott:顺便说一句,与事实上的标准不同的单个站点并不是说该标准不存在的论据。事实上,查看 SO,您的所有用户凭据和其他所有内容都以明文形式传递,并且他们已经公开表示安全不是这里的优先事项......所以如果您追求一个真正安全的系统,我不会接受SO 做事方式的经验教训。【参考方案3】:@NickHeidke 和@Chris Lively 提交的答案都很棒,让我得出了一些我想分享的结论。
首先,关于可用性:密码字段保留其值的唯一位置是在“注册”表单上,当表单未通过验证且验证问题未通过时与密码或确认密码有关。它绝对不适合“登录”表单、“更改密码”表单或任何其他地方。因此,Html.Password
忽略 ModelState
的事实非常合理。
其次,关于安全性:除非仔细实施,否则“注册”表单将保存在浏览器的导航历史记录中。按“返回”将重新发送您的密码,这可能会导致验证失败,并返回填写密码的表单。填写密码这一事实并不是安全漏洞,因为它与浏览器已经保存和发送的密码相同。您可以“查看源代码”来查看密码,但您也可以查看页面请求以查看密码(例如,使用 FireBug)。
当然,当您看到填写了密码时,“查看源代码”更容易、更明显,但我想说更好的解决方案是以不保存到的方式实现“注册”表单浏览器历史;例如,PRG Pattern with a Validation workaround,但这是一个完全不同的主题!
我的结论是这样的:密码字段在显示时应该几乎总是清晰的。 如果您想提高可用性并且不想让用户重新输入密码,请先尝试使用客户端验证! 最后,如果您真的知道自己在做什么,并且真的想提高可用性,那么可以将这些值保留在注册表单上。但这可能不是最好的主意。
【讨论】:
是的。如果验证失败与密码无关,您可以填写密码。特别是在您接受复选框、验证码、文档验证器等的情况下。正如您所总结的,虽然没有安全漏洞,但最好默认为空白,因为一般用例。请注意:客户端验证是一种管理场景的“简单”方法,它也提高了可用性,但它与填充内容没有直接关系。行为不仅适用于密码,验证码也是一个很好的候选者。密码确认错误后重置验证码,简单同样痛苦。 对这种设计的可能改进是将密码字段移动到仅包含密码的后续屏幕。这样您就可以对所有注册字段进行后端验证,然后让用户单独处理密码。【参考方案4】:虽然我同意在登录时这种行为是明智的,但感觉它在创建用户帐户时提供了极差的用户体验。当表单上有许多字段并且用户在一个或多个字段中出错时,必须重新输入密码(新密码和确认密码)非常烦人,并且当密码字段在视图中不可见时不滚动它很容易忘记重新输入。
【讨论】:
是的,这正是问题的原因。 “我觉得这种行为会降低可用性,并且希望密码字段的行为方式与文本框字段相同——在存在验证错误时保留输入的值。” 来自@ScottRippey 的回答:当表单未通过验证时,密码字段保留其值的唯一地方是“注册”表单,并且验证问题与密码或确认密码无关。【参考方案5】:@Scott Rippey 给出的答案确实解释了很多。 您不能保留 @Html.Password 或 @Html.PasswordFor 的值,因为它在帮助程序本身中是硬编码的。有关更多信息,请参阅此链接。 retain password value.
解决方案: 您可以做的是将 Password 或 PasswordFor 框替换为
@Html.TextBoxFor(x => x.Password, new type = "password" )
【讨论】:
【参考方案6】:是的,您可以使用文本框而不是使用密码来保留密码字段值
@Html.TextBoxFor(x => x.Password, new type = "password" )
【讨论】:
以上是关于如果表单未通过验证,密码字段是不是应保留其值?的主要内容,如果未能解决你的问题,请参考以下文章