我是一名 Web 开发人员,并且一直在尝试学习一些关于安全性的知识,而似乎最大的原则就是尽可能少地信任客户端。这是一个正确的想法吗?规则不涵盖/不正确的情况是什么?
不信任客户?
“不信任客户端”意味着来自客户端的任何输入都可能被恶意发送,因为您无法控制它。因此,例如,这意味着必须在服务器端检查和执行身份验证和授权。如果您让客户端验证任何内容然后告诉您“ok”,那么恶意用户可能会跳过验证并将“ok”发送到服务器。一般来说,这也意味着任何类型的输入验证都必须在服务器端强制执行。
如果您不信任客户端,则基本上可以防止与输入验证相关的所有漏洞。但这当然是不够的。还有其他类型的漏洞。例如,有输出清理,有可能导致 CSRF(跨站点请求伪造)或点击劫持的混淆代理问题,有与错误的身份验证方案、错误配置等相关的问题。在某些情况下,甚至定时攻击也可能是一个问题。如您所见,并非所有安全问题都可以通过输入验证来解决。
但是,不建议依赖客户端验证,因为用户可以使用 Web 代理绕过该验证。流量也容易受到 MITM 的影响,并且可能被用户以外的其他人篡改。
正如您所问的,您何时可以在知情的情况下放宽此规则以接受风险,这可能是在与受信任的第三方进行通信时。为了安全起见,您可以检查 ORIGIN 和 DOMAIN。
一种情况是,当您使用 2 路 SSL 握手进行身份验证、保留审核日志、Web 服务将输入视为文本而不是命令并且所有通信都通过专用/安全通道进行时,您可以放松一点。
客户往往是克星。但主要是无效/假客户。安排可能是学习如何信任客户而不是以相同的方式对所有客户进行分类。
如前所述,“不信任客户端”背后的想法是客户端可以发送任何内容。它受用户的摆布,用户可能是攻击者,因此必须将其视为潜在的攻击者。
这在 Web 开发中至关重要,但几乎同样适用于任何其他客户端/服务器架构。(通过戳网页比通过逆向工程或反编译和修改或重新实现桌面应用程序更容易利用。)
我们举一个简单的例子:
坐在阳光明媚的海滩上喝您选择的饮料,您正在实现一个网页,用户可以在其中输入文本长度和文本,并且 Web 服务器通过发送回客户端指定的文本中的任意多个字符来响应。您还在网页上进行了一些验证,以确保用户不会要求服务器发回比他们输入的文本更长的文本,用户不会提供负长度,以及其他一些健全性检查.
因此,如果我发送“5”和“Hello World”,我会返回“Hello”。如果我发送“8”和“thunderstorm”,我会返回“thunders”。如果我发送“0”和任何东西,我会得到一个空字符串。如果我尝试发送“-2”和任何内容,页面会出错并且不会发送任何请求。等等。
这听起来既安全又简单,不是吗?只要每个人都遵守规则,就没有问题。
但是,攻击者通常不遵守规则。这是一个黑暗、暴风雨的夜晚,Carol 浏览了您的网页并开始使用它。通过对客户端验证脚本稍作调整,或通过直接调用 Web 服务,或通过其他一些客户端技巧,她能够发送请求服务器发送回比她发送的更多数据的请求给它。所以她可能会发送“20”和“Cookie”。现在会发生什么?
只要您将用户输入与其他所有内容分开,您的典型托管语言要么不允许这样做,要么会相对优雅地处理它。但是,如果您将客户端提供的数据与其他数据相结合,或者您使用的是较低级别的语言,我们基本上只是重新发明了对 OpenSSL 的 HEARTBLEED 攻击。对这个特定示例的修复很简单:只需在服务器上添加一个边界检查,这样客户端就不能请求比它本身发送的更多的数据,无论客户端在做什么恶作剧。
这也可以推广到任何其他情况。您的系统是否要求输入的时间段不重叠?如果它们重叠,它可能会做一些奇怪的事情,除非你有检查。它是否要求货币金额为正且大于零?我会向甜甜圈打赌,如果不满足这些条件并且您不确定它们是否满足,就会发生一些计划外的事情。它是否要求链接指向有效的 Internet URL(<a href="script:alert('boo!')">try it!</a>
不允许)?您最好确保该服务器端,因为没有什么会迫使用户控制的客户端拒绝用户的此类输入。等等等等。
作为一般原则,任何客户端验证都应该被认为是对用户的方便,并且客户端提供的任何值至少应该被认为是潜在的不可信。因此,您应该始终计划并应对未执行客户端验证或客户端正在与服务器玩游戏的情况。
这就是“不要相信客户”的全部意义所在。
我可以看到这不适用的唯一情况是服务器与客户端相比功能严重不足(因此重新运行验证将非常昂贵),或者您根本不在乎。除了极其狭窄的情况外,我没有看到任何一个应用程序。如果他们确实适用,您可能会知道。