如何验证用户输入

信息安全 javascript 网站 数据验证
2021-08-27 16:08:49

我很难理解作为开发人员可以在哪里验证用户发送给我的数据。举个简单的例子,我有一个网页,其中有一个包含三个字段的简单表单:

Name -> text field
email -> text field
Lunch Choice -> Dropdown field
  values:
    Burger
    Hotdog
    Pizza

用户提交表单后,他们的姓名和订单将被保存,他们可以在第二天领取午餐。

如果我是一个恶毒的用户,并且我访问了这个站点并编辑了 html,说比萨的价值实际上是沙拉,而选项的价值是其他一些价值。

现在,当提交并保存表单时,一些不良数据正在进入数据库。

我在哪里可以阻止这个?

我的第一个想法是进行一些 javascript 验证,与服务器进行 ajax 调用以确保请求的值有效。这听起来是个好主意,除了 javascript 在客户端而不是服务器上运行,因此也容易受到攻击。

如果我让用户提交表单,然后在服务器上处理提交并在那里进行检查,那么用户已经提交了表单,所以我需要做某种重定向回他们的表单?

我确定我不是第一个对输入证券有疑问的开发人员,我想知道是否有处理此类问题的最佳实践。

提前致谢!

4个回答

是一个基本问题。您应该在服务器端验证所有内容。后端是进行验证的正确位置。当然,javascript 验证对于将标准用户驱动到正确的值也很有用,但对于黑客(或任何高级用户)来说很容易被欺骗。

假设您在后端有一个数据库,并且您知道可以为每个用户分配哪种食物。您应该检查接收到的值是否存在于有效记录集中。如果没问题,您可以继续您的工作流程。如果没有,您可以显示错误并停止该过程。

您将面临的另一个重要问题不仅是逻辑绕过。注意特殊字符并过滤所有危险的东西,如单引号、双引号、破折号、分号等……否则您将面临 sql 注入或 XSS(跨站点脚本)攻击。那是另一个故事……但我提到它是因为它也很重要。

奥斯卡已经发布了一个很好的答案,但为了回答你的一些问题:

我的第一个想法是进行一些 javascript 验证,与服务器进行 ajax 调用以确保请求的值有效。这听起来是个好主意,除了 javascript 在客户端而不是服务器上运行,因此也容易受到攻击。

这太复杂了。就像你说的,这很容易受到攻击,此外,当浏览器中禁用 Javascript 时,它会完全失效。

如果我让用户提交表单,然后在服务器上处理提交并在那里进行检查,那么用户已经提交了表单,所以我需要做某种重定向回他们的表单?

是的。

传统上,如果你想使用 JS 验证,你会在前端提出更正建议。“嘿,您的电子邮件地址中没有@。请输入一个有效的电子邮件地址。” 这样可以节省您上面描述的往返行程。Javascript 不是为了加强安全性,而是为了加强用户体验。

然后,一旦他们输入了有效的电子邮件地址并提交了他们的内容,他们就再也无法绕过您的机制了。球在你的球场上。在服务器上,你检查 Oscar 提到的讨厌的东西,清理所有字符串,如果你检测到有问题,你确实将用户返回到输入页面(如果你想要更好,你使用 sanitized重新填充表单字段)。

所以此时,即使 JS 被禁用,用户仍然会收到一条 HTML 消息,说明他们需要输入有效的电子邮件地址。是的,这确实意味着制作两组不同的错误消息——一组用于 JS,一组用于 HTML(这就是为什么我个人从不关心 JS 验证的原因)。

请注意重新显示他们提供给您的任何内容。如果他们在字符串中嵌入了恶意代码,那么在不先清理它们的情况下将它们显示在结果页面上可能会以糟糕的方式结束。

来自系统外部的任何数据都跨越了“信任边界”,需要在系统内部进行验证。这意味着需要服务器端输入验证。

执行输入验证意味着检查您的输入以确保您可以安全地处理它。棘手的部分是,通过验证它,您已经在进行一些次要处理,这可能会产生隐藏的漏洞。所以验证输入有一个特定的顺序。

第一步是验证输入的长度。大多数输入将暂时落在有限的缓冲区中。确保复制到缓冲区的输入量不超过缓冲区的大小——如果输入过多而缓冲区不足,则会产生经典的“缓冲区溢出”问题;利用这些是黑客的主要手段。

下一步是确保数据采用您期望的格式。如果您需要一个数字,请确保字节仅包含数字和您允许的数字符号,例如加号、减号、分隔符、小数点、货币符号等。请注意,这些是特定于语言环境的:在美国,a百万美元可以输入为$1,000,000.00,而在德国,一百万欧元可以输入为1.000.000,00€如果您需要字母数字字符和数字,请使用“已批准列表”来仅接受您期望的字符。

依赖好角色的批准列表比坏角色的拒绝列表更安全,因为攻击者将来会学习新的攻击。有可能一个意想不到的角色明天会允许我们今天不知道的注入攻击。

请注意,如果您在检查输入长度之前反转这些检查并测试特殊字符,您的验证代码可能容易受到缓冲区溢出的影响。这就是为什么以正确的顺序执行它们很重要的原因。

应该使用输入检查来防止注入攻击似乎很直观(SQL 注入是攻击者输入了一些不好的东西,例如' OR 1=1;DROP TABLE STUDENTS--),但这并不总是可行的。有人可能会试图通过将撇号放入拒绝列表来阻止这种注入,但撇号通常是有效数据,例如 O'Brian 的名字。此外,攻击者通常可以使用 URL 编码等其他策略绕过已批准列表。因此,我们在与 SQL 接口的代码中添加了另一道防线。该代码需要负责尽可能安全地执行查询。这可能是使用参数化 SQL 查询、ORM 或其他防御策略。这样,如果攻击者想出绕过批准列表的方法,参数化 SQL 仍应阻止他们。

注入攻击也不限于 SQL。攻击者会尝试将路径分隔符注入文件名、shell 分隔符(如管道(|))、XML 分隔符、URL 等;你接受的任何东西都可能受到滥用。任何解释用户输入的代码都需要编写以避免此类问题。

验证后的步骤是对输入进行编码以保护输出。例如,如果您要接受<>稍后在网页上输出结果,您需要确保您对符号进行 HTML 编码,以免无意中造成攻击者可以植入的漏洞<script>attack!</script>在输出页面上。

为避免将意外数据写入数据库,请在提交表单后立即通过接收它的脚本/进程在服务器端对其进行验证。

从外部来源接收到的任何数据都应被视为潜在恶意数据,因此,接收它的每个进程都应在处理之前对其进行清理。

客户端验证的目标是提供一个用户友好的界面。服务器端验证的目标是确保用户的输入是安全的。

我推荐 OWASP 的Input Validation Cheat SheetSQL Injection Prevention Cheat Sheet