HTML5 输入模式验证对于客户端验证是否足够(甚至相关)?

信息安全 Web应用程序 javascript html-5 数据验证
2021-08-16 08:36:44

HTML5 的一个有趣特性是<input pattern="" /> 属性,它允许浏览器根据开发人员提供的正则表达式验证输入字段的值。

随后,这将绑定到字段ValidityState,然后可以查询 UX 反馈,以防止无效提交等。

服务器实现相同的前置条件验证(以及 XSRF)。

鉴于此,这种客户端验证模式是否成熟且足够,还是出于某种原因仍然需要截取整个表单并在提交之前对其进行传统的逐字段验证?

4个回答

从安全角度来看,您需要重新验证服务器上的所有内容。无论 HTML5 功能变得多么漂亮和高级,情况都会如此。您根本无法信任客户。你不知道它是否会遵循 HTML5 规则。你甚至不知道它是不是一个浏览器。

因此,即使您使用 HTML5 功能,您是否应该在提交之前使用您自己的 JS 客户端验证整个表单?从安全的角度来看,这并不重要。无论如何,客户端验证的安全值为零 - 见上文 - 因此从安全角度来看,您可能根本不费心去做。

客户端验证纯粹是关于用户体验。如果您自己的 JS 验证或内置的 HTML5 可以实现最佳 UX 是一个有趣的问题,但它不是这里的主题。

带有代码和屏幕截图的解释

给出的答案很棒。但我想用一些代码/截图来说明这一点。

底线是最终用户可以操纵任何客户端(禁用/完全摆脱/绕过或修改)。因此,从安全角度来看,任何形式的“客户端验证”都是完全没用的。您必须始终验证服务器端的东西。

假设您有一个这样的 HTML 表单:

<form method="post" action="index.php">
    <input type="email" name="emailAddress" required>
    <button type="submit">Submit form</button>
</form>

这是要求提供电子邮件地址 ( type="email"),表示它是必填字段 (required属性)。

如果我尝试提交空的或不是电子邮件地址的内容,则会出错,例如

在此处输入图像描述

从安全的角度来看,为什么这没有用?用户所要做的就是操作 HTML。他们可以通过保存网页副本、编辑它然后重新打开它来做到这一点。或者,他们可以使用浏览器中的开发者工具来编辑标记。他们可以这样做,因为它是客户端代码,并且在他们的控制范围内

假设我将表单的标记更改为:

<form method="post" action="index.php">
    <input type="text" name="emailAddress">
    <button type="submit">Submit form</button>
</form>

我现在说该emailAddress字段是类型text(即 not email)并且不是必填字段(已删除required属性)。

所以我可以将它提交给服务器。文本字符串“andy”显然不是有效的电子邮件地址。但是由于我们还没有服务器端验证,它会很高兴地发布到服务器上,然后如果使用 PHP 代码$_POST['emailAddress'],它的数据就会有“andy”。

在此处输入图像描述

我也可以在没有任何数据的情况下提交表单,在这种情况下,应用程序 - 没有任何服务器端验证 - 变量中将有一个空字符串$_POST['emailAddress']

这是可能的,因为我作为最终用户可以操纵客户端代码。

为什么服务器端验证是安全的?最终用户无法操作服务器端代码(除非服务器已被入侵,但这是一个非常独立的问题,而不是像为普通人操作 HTML 那样容易的事情)。

因此,在 PHP 中,我可以进行如下检查:

if (!filter_var($_POST['emailAddress'], FILTER_VALIDATE_EMAIL)) {
    die('Invalid email address');
}

尽管这是一种不友好的错误处理方法,但如果用户提交“andy”而不是有效的电子邮件地址,它将停止脚本执行。因此,应用程序永远不会使用“andy”作为它所期望的电子邮件地址的变量。由于最终用户无法操作上面的 PHP 代码,他们绕过验证的可能性较小。这是服务器端验证 - 最终用户无法(轻松)更改它,因为更改它不在他们的控制范围内。

那为什么还要麻烦客户端验证呢?客户端验证对于“漂亮”的用户界面增强功能很有用,或者例如错误消息/禁用表单字段。例如,可以说在第一个屏幕截图上显示该消息是一个很好的 UI 功能,以防用户输入错误的电子邮件地址。表单甚至不会在第一组条件下提交,从而减少了发送到服务器的不必要数据。但最终,最终用户可以更改客户端所做的任何事情。所以它绝不是安全的,当涉及到安全实践时,你永远不应该想到“客户端验证”。任何客户端验证主要仅用于用户界面增强。

客户端验证对于客户端应用程序也很有用(当它不请求/发布到服务器时)。例如,如果您有上面给出的表单,然后通过使用 JavaScript/jquery 读取它来在页面上显示emailAddressa的内容。<div>如果用户输入了诸如<script>alert('hello!');</script>但没有验证之类的内容,那么当 JavaScript 尝试将字段中的值写入<div>. 在这种情况下,没有任何东西被发布到服务器 - 这一切都发生在客户端 - 所以客户端验证在这种类型的场景中再次有用。然而,同样的原理也适用于用户仍然可以禁用验证。这里的影响较小,因为如果他们确实运行了上述代码,它只会发生在他们的本地机器上,而不是应用程序的其他用户。例如:

在此处输入图像描述

使用以下代码(没有任何验证):

<form method="post" action="#">
    <input type="text" name="emailAddress" id="emailAddress" size="100">
    <button type="submit">Submit form</button>
</form>

<div id="test"></div>

和 jquery 将表单内容写入<div>具有 ID 的test

$(document).ready(function() {
    $("button").click(function(e) {
        e.preventDefault();

        $("#test").html($("#emailAddress").val());
    });
});

正如 Anders 所说,客户端验证与应用程序的安全性无关,但从用户体验的角度来看非常重要。

客户端验证的重点是为用户在填写表单时提供更流畅和更轻松的时间,我知道的最好的例子是 jQuery Masks 插件,它可以用来限制输入大小和模式,同时为用户提供视觉反馈。这可能会帮助您解决非安全漏洞,例如以与服务器端预期不同的模式从用户那里接收数据,但很容易被任何攻击者忽略。

tl; dr - 没有足够的客户端验证,根本不信任客户端,只需在处理之前处理服务器上收到的所有内容。

到目前为止,没有一个答案真正提到它,所以让我补充一下:

HTML 中的任何内容与安全性完全无关的原因是任何攻击者都可以轻松地创建请求,甚至无需在浏览器中加载任何 HTML(通过在控制台中输入请求或使用某些插件)。实际上,甚至无需启动浏览器(c/fcurlwget任何编程语言)。

这些东西的相关部分是HTTP请求。不是 HTML 有效负载。除了 TLS/SSL 层之外,没有任何东西可以保护实际创建请求的人的 HTTP 请求内容。

因此,任何和所有安全都必须发生在服务器端。任何客户端检查都是为了更好的用户体验(跳过往返)。