JavaScript: 客户端验证 vs. 服务器端验证

209

客户端验证和服务器端验证哪个更好?

在我们的情况下,我们使用:

  • jQuery 和 MVC。
  • JSON 数据在视图和控制器之间传递。

我做的大部分验证都是用户输入数据时进行的。例如,我使用 keypress 事件来防止文本框中输入字母、设置最大字符数以及确保数字在范围内。

我想更好的问题应该是,相比客户端验证,是否有进行服务器端验证的好处呢?


大家都给出了很好的答案。我们的网站受到密码保护,用户数量很少(小于50)。如果他们没有运行 JavaScript,我们会派忍者去解决。但是,如果我们要为所有人设计一个网站,我同意在两个方面都进行验证。


3
JavaScript可以被禁用。 - Enrico Murru
没有确保能够阻止禁用JavaScript的用户的方法。如果用户启用了JS并访问您的页面,然后禁用了它,您将无能为力。(好吧,您可以使用JS来实现提交控件,以便在这种情况下停止工作,但是这可以像其他所有东西一样被绕过。) - Stewart
13个回答

388

正如其他人所说,你应该同时进行客户端和服务器端验证。原因如下:

客户端

你希望首先在客户端验证输入,因为你可以给普通用户提供更好的反馈。例如,如果他们输入了无效的电子邮件地址并移动到下一个字段,你可以立即显示错误消息。这样用户可以在提交表单之前纠正每个字段。

如果你只在服务器端进行验证,他们必须提交表单,得到错误消息,并尝试查找问题。

(通过重新呈现包含用户原始输入的表单来缓解这种痛苦,但客户端验证仍然更快。)

服务器端

你希望在服务器端进行验证,因为你可以防止恶意用户,他们可以轻松绕过你的JavaScript并向服务器提交危险的输入。

相信你的UI非常危险。他们不仅可以滥用你的UI,而且可能根本没有使用你的UI,甚至没有使用浏览器。如果用户手动编辑URL,或运行自己的Javascript,或使用其他工具调整其HTTP请求怎么办?例如,如果他们从curl或脚本发送自定义HTTP请求,会怎样?

这不是理论,例如,我曾经在一个旅游搜索引擎上工作过,该引擎通过发送POST请求将用户的搜索重新提交给许多合作航空公司、巴士公司等,就好像用户填写了每个公司的搜索表单一样,然后收集和排序所有结果。这些公司的表单JS从未被执行过,在返回的HTML中提供错误信息对我们来说至关重要。当然,API会很好,但这是我们必须做的。

不允许这样做不仅在安全方面是幼稚的,而且不符合标准:客户端应该允许以任何方式发送HTTP,并正确响应。这包括验证。

服务器端验证对于兼容性也很重要——即使使用浏览器,也不是所有用户都启用JavaScript。

附录-2016年12月

有些验证甚至在服务器端应用程序代码中都无法正确执行,而在客户端代码中完全不可能,因为它们依赖于数据库的当前状态。例如,“没有其他人注册了该用户名”,或者“您正在评论的博客文章仍然存在”,或者“没有现有预订与您请求的日期重叠”,或者“您的帐户余额仍足以支付该购买。” 只有数据库可以可靠地验证依赖于相关数据的数据。 开发人员经常搞砸这个, 但PostgreSQL提供了一些好的解决方案


3
“@kidmosey说:“这明显违反了DRY原则”,这意味着像我们程序员这样的人会感到痛苦。但是想象一下一个注册表单,如果在客户端代码中复制“电子邮件地址必须包含@”这一知识意味着用户可以更快地获得反馈并且有更多用户进行注册,从而每年产生额外10万美元的收入,这就超过了额外维护成本。 DRY是一个非常好的原则,但不是唯一需要考虑的。通过成本效益分析来衡量代码质量真正的标准在于它对用户和组织的服务程度。” - Nathan Long
1
@ArunRaaj 是的,这样做可以捕获大部分问题,但它并不是100%可靠的。如果两个用户同时填写表单,他们可能都会被告知“user1”是一个可用的用户名。当他们提交时,除非您在服务器端重新检查,否则他们将都获得相同的用户名。即使在服务器应用程序代码中进行检查也可能出现相同的问题:两个请求进来,第一个请求检查数据库并被告知OK,第二个请求检查数据库并被告知OK,第一个请求被保存,第二个请求被保存为重复项。只有数据库唯一约束条件才能保证唯一性。 - Nathan Long
1
Nathan,我刚刚看了你2016年的编辑。你指出了需要从数据库进行验证的正确性。但是你知道,数据库验证实际上属于服务器端验证,其中您检查用户名是否可用。 - KMX
1
@NathanLong 对于对竞态条件敏感的数据进行验证并不像这句话所说的那样棘手。虽然正确地执行很麻烦,但可以创建一个使用同步资源请求的预订机制。因此,如果用户键入“usernameA”,则在服务器上进行唯一性检查,以防止多个同时调用以检查是否唯一;如果唯一,则还应将其保留,并分配给客户端一个临时令牌,该令牌也会在同一会话ID测试不同用户名时释放。令牌应在合理时间后过期。例如:TicketMaster座位预订。 - Elaskanator
1
@KMX 我试图区分像数据库唯一约束这样可靠的东西和不可靠的东西,比如让服务器端应用程序代码执行SELECT后跟一个INSERT,这意味着有可能在两个操作之间进行了另一个INSERT。锁定表可以防止这种情况发生,但唯一约束更好。 - Nathan Long
显示剩余7条评论

86

是的,客户端验证可以完全被绕过,总是如此。您需要同时进行客户端和服务器端验证,以提供更好的用户体验,并确保您获得的输入实际上已经通过验证,而不仅仅是客户端假定已通过验证。


46

我只是想再重复一遍,因为这非常重要:

始终在服务器上进行验证

并添加JavaScript来提高用户响应能力。


34

在进行服务器端验证和客户端验证时,服务器端验证的好处是客户端验证可以被绕过/操纵:

  • 最终用户可能已关闭 JavaScript
  • 数据可能会通过定制应用程序直接发送到您的服务器,而不是使用您的网站的人发送
  • 您页面上的 JavaScript 错误(由许多原因引起)可能导致某些但不是全部的验证运行

总之,务必始终首先进行服务器端验证,然后考虑客户端验证作为增强最终用户体验的“额外”功能。


19

必须始终在服务器端进行验证。

对于用户来说,客户端的验证可以提供更好的体验,但是其极不安全。


9

好的,我还有一些空间来回答。

除了Rob和Nathan的答案之外,我想补充一点,那就是客户端验证很重要。在为您的Web表单应用验证时,必须遵循以下指南:

客户端

  1. 必须使用客户端验证,以过滤来自您网站上真实用户的真实请求。
  2. 客户端验证应用于减少服务器端处理中可能发生的错误。
  3. 应使用客户端验证以最小化服务器端往返次数,以节省带宽和每个用户的请求数量。

服务器端

  1. 您不应假设在客户端成功完成验证后100%完美。即使只服务于不到50个用户。你永远不知道你的哪个用户/员工会变成“邪恶”,知道你没有适当的验证措施,会进行一些有害活动。
  2. 即使在验证电子邮件地址、电话号码或检查某些有效输入方面非常完美,它也可能包含非常有害的数据。无论正确与否,都需要在服务器端过滤这些数据。
  3. 如果绕过客户端验证,则您的服务器端验证将挽救您免受任何潜在的对服务器端处理的损害。最近,我们已经听说了很多SQL注入和其他技术的故事,这些技术可能被应用以获得一些邪恶的好处。

两种类型的验证在各自的范围内发挥着重要的作用,但最强大的是服务器端。如果您在单个时间点收到10k个用户,则肯定会筛选您的Web服务器接收的请求数量。如果您发现有一个简单的错误,例如无效的电子邮件地址,则他们会再次提交表单并要求用户进行更正,这将耗费您的服务器资源和带宽。因此,最好应用JavaScript验证。如果禁用了JavaScript,则您的服务器端验证将挽救您,并且我敢打赌只有极少数用户可能会意外地禁用它,因为99.99%的网站都使用JavaScript,并且在所有现代浏览器中默认启用。


我见过有些人完全忽略了对抗代码注入的保护措施,更不用说只在客户端进行保护了。而且,没有提到代码注入就不能完整,这里附上一个链接:https://xkcd.com/327/ :) - Stewart

9
您可以进行服务器端验证,并返回一个JSON对象,其中包含每个字段的验证结果,使客户端JavaScript最小化(仅显示结果),仍然具有用户友好的体验,而无需在客户端和服务器上重复自己。

3
用户友好?也许吧。几乎瞬间且无缝?可能不是。 - quadrupleslap

4

4

客户端应使用基本验证,通过HTML5输入类型模式属性进行验证。尽管这些仅用于渐进式增强以获得更好的用户体验(即使在<IE9和Safari上不支持它们,但我们不依赖它们)。但主要的验证应该在服务器端进行。


但是主要的验证应该在服务器端进行。不是应该,而是必须。 - Stewart

3
我发现了一个有趣的链接,区分了“粗糙的、系统性的、随机的”错误。
客户端验证非常适合防止粗糙和随机错误。通常设置任何输入的最大长度。不要模仿服务器端验证规则;提供自己的粗略、经验法则验证规则(例如在客户端200个字符,在服务器端特定业务规则下小于200个字符的n个字符)。
服务器端验证非常适合防止系统性错误,它将强制执行业务规则。
在我参与的一个项目中,通过ajax请求在服务器端完成验证。在客户端上,我相应地显示错误消息。
进一步阅读:粗略的、系统性的、随机的错误:https://answers.yahoo.com/question/index?qid=20080918203131AAEt6GO

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接