【问题标题】:How can I validate/secure/authenticate a JavaScript-based POST request?如何验证/保护/验证基于 JavaScript 的 POST 请求?
【发布时间】:2011-02-03 10:56:43
【问题描述】:

我正在帮助开发的产品基本上是这样工作的:

  • 网络发布者在其网站上创建了一个新页面,其中包含来自我们服务器的<script>
  • 当访问者到达该新页面时,<script> 会收集页面的文本内容并通过 POST 请求(跨域,在 <iframe> 中使用 <form>)将其发送到我们的服务器。
  • 我们的服务器处理文本内容并返回一个响应(通过JSONP),其中包括一个HTML 片段,其中列出了指向Web 相关内容的链接。此响应被缓存并提供给后续访问者,直到我们从同一 URL 收到另一个带有文本内容的 POST 请求,此时我们重新生成一个“新”响应。这些 POST 仅在我们缓存的 TTL 过期时发生,此时服务器会表示并提示页面上的 <script> 再次收集和 POST 文本内容。

问题是这个系统似乎天生就不安全。理论上,任何人都可以欺骗将页面内容发送到我们的服务器的 HTTP POST 请求(包括referer 标头,因此我们不能只检查它)。这可以包括任何文本内容,然后我们将使用这些内容为该页面生成相关内容链接。

确保这个安全的主要困难是我们的 JavaScript 是公开可见的。我们不能使用任何类型的私钥或其他神秘的标识符或模式,因为这不是秘密。

理想情况下,我们需要一种方法来验证对应于特定网页的 POST 请求是否真实。我们不能只抓取网页并将内容与发布的内容进行比较,因为让 JavaScript 提交内容的目的是它可能位于登录系统之后。

有什么想法吗?我希望我已经很好地解释了这个问题。提前感谢您的任何建议。

【问题讨论】:

  • @Bungle:您提到“我们不能使用任何......神秘的标识符”,但这种机制可以公开观察,同时仍然保持安全。您可以生成从网络发布者的域(例如“foo.bar.com”)派生的加密密钥。您的服务器端代码可以解密密钥以提取域(除其他外),并且您的客户端代码显然可以访问window.location 以验证该脚本仅被授权这样做的域包含。类似于 Google 对其客户端脚本 api 所做的事情:code.google.com/apis/maps/documentation/premier/…
  • @Bungle:没关系,我想我明白你在问什么。您想保护实际的 POST 请求,而不是脚本包含。
  • 这不是一个容易解决的问题。我需要有关您的申请的更多信息。什么是有效的文件上传?您是否强制用户登录?您只接受来自特定域的帖子吗?您必须更具体地说明什么是有效与无效。
  • @Crescent Fresh:没错。我们的想法是任何人都可以生成模拟我们的 POST 请求,无论他们是否在网页中运行我们的<script>。只要他们的请求具有正确的标头信息和内容,我们的服务器就会接受它并将生成的 HTML 绑定到他们指定的 URL。
  • @The Rook:本质上,我们想要一种方法来确保我们的服务器收到的 POST 请求实际上是由 Web 发布者页面上的 <script> 元素发起的(即,来自我们的客户),而不仅仅是恶意方生成的 POST 请求。不涉及登录/身份验证或文件上传。我们的<script> 出现在网页上,执行时收集其文本内容并将其发布到我们的服务器,在服务器上使用页面的 URL 作为键对其进行索引。恶意用户可以...

标签: javascript security authentication cross-domain jsonp


【解决方案1】:

首先,我会按照其他人的建议验证域(可能还有“服务器配置文件”),并且显然非常严格地验证 POST 的内容(我希望你已经在这样做了)。

如果您将脚本文件的 URL 指向由您的服务器动态生成的内容,您还可以包含一个时间敏感的会话密钥,以便与 POST 一起发送。这不会完全挫败任何人,但是如果您能够使会话足够快地过期,那么利用起来将更加困难(如果我正确理解您的应用程序,会话应该只需要为用户持续足够长的时间加载页面后输入内容)。

在输入这个之后,我意识到它基本上就是 avlesh already suggested 加上一个过期时间。

【讨论】:

    【解决方案2】:

    这个怎么样? - 第三方网站包含的<script/> 标签具有动态src 属性。因此,它不是加载一些静态 Javascript 资源,而是来到您的服务器,生成一个唯一键作为网站的标识符,并将其发送回 JS 响应中。您将相同的密钥保存在用户会话或数据库中。这段JS代码创建并提交的表单也会提交这个关键参数。您的后端将拒绝任何与您的数据库/会话中的密钥不匹配的 POST 请求。

    【讨论】:

      【解决方案3】:

      这没有确凿的证据。然而,在大枪不存在的地方,主要的烦恼可以。黑客喜欢挑战,但他们更喜欢简单的目标。烦到让他们放弃。

      Google 和其他公司通过广告词有效地做到了这一点。创建一个 api 令牌并让他们发送。对使用您的脚本的站点进行“验证”过程,要求此脚本的注册者允许在使用脚本之前对其站点进行概要分析。然后,您可以收集有关服务器的所有信息,如果服务器配置文件与记录的配置文件不匹配,则可以提出请求。

      获取有关浏览器和客户端的所有信息并为其创建配置文件。如果有任何可能是浏览器欺骗,请放弃该请求。如果配置文件重复但 cookie 消失,则忽略输入。如果您在短时间内从令牌收到多个请求(即黑客尝试固有的快速页面刷新),请忽略该请求。

      然后更进一步,ping 实际域以验证它是否存在并且是授权域。即使页面在登录之后,域仍然会响应。这本身不会阻止黑客,但它是在服务器端完成的,因此是隐藏的。

      此外,您可能会考虑分析页面的内容。如果专注于厨房用具的网站开始发回成人约会内容,请举起危险信号。

      最后,当一个错误请求传入您已将其分析为错误请求时,根据您知道的良好数据(该页面的 24 小时旧版本)从对该页面的良好请求发送 JSONP等等。)。不要告诉黑客你知道他们在那里。表现得好像一切都很好。他们需要很长时间才能弄清楚这一点!

      这些想法都不能满足您的问题的确切需求,但希望它会激发您的一些阴险和创造性思维。

      【讨论】:

        【解决方案4】:

        您可以抓取该网站,如果您收到包含您的脚本的代码 200 响应,只需使用该抓取。如果不是,您可以解析来自“客户端代理”的信息,这样问题就出在您无法抓取的网站上。

        为了在这些情况下提高安全性,您可以让多个用户发送页面,并过滤掉在最少数量的响应中不存在的任何信息。这还将具有过滤掉任何用户特定内容的额外好处。还要确保注册您要求执行代理工作的用户,并确认您只收到您要求执行该工作的用户的页面。您还可以尝试确保非常活跃的用户不会获得更高的工作机会,这将使“钓鱼”变得更加困难。

        【讨论】:

          【解决方案5】:

          您所描述的系统的主要弱点是您“获得”了页面内容,为什么不自己去获取页面内容?

          1. 网络发布者在其网站上创建了一个新页面,其中包含来自您服务器的脚本。
          2. 当访问者到达该新页面时,该脚本会向您的服务器发送一个获取请求。
          3. 您的服务器会去获取页面的内容(可能通过使用引用标头来确定请求的来源)。
          4. 您的服务器处理文本内容并返回响应(通过 JSONP),其中包含一个 HTML 片段,列出了指向 Web 相关内容的链接。此响应被缓存并从服务器端缓存/代理提供给后续访问者
          5. 当缓存版本的 TTL 过期时,代理会将请求转发到您的应用程序,整个周期从第 3 步重新开始。

          这会阻止恶意内容被“馈送”到您的服务器,并允许您提供某种形式的 API 密钥,将请求和域或页面联系在一起(即 api 密钥 123 仅适用于 mydomain.com 上的引荐来源网址 - 其他任何内容都是显然是欺骗的)。由于缓存/代理,您的应用程序在某种程度上也受到了任何形式的 DOS 类型攻击的保护,因为每次缓存 TTL 过期时页面内容只处理一次(现在您可以通过扩展 TTL 来处理不断增加的负载,直到您可以带来额外的处理能力)。现在您的客户端脚本非常小而简单 - 不再需要抓取内容并发布它 - 只需发送一个 ajax 请求并可能填充几个参数(api key / page)。

          【讨论】:

          • 问题中详细说明了此答案中第3步的问题;被抓取并发送到其他地方的页面可能在登录之后。因此,用户已经登录到一个正在被抓取/被盗的页面,如果不冒充用户、登录、导航到它等,收集内容的服务器无法通过简单的 GET 访问。我怀疑这是否不过,这是一个很好的用例,我们被要求提供帮助。
          【解决方案6】:

          网络发布者也可以在他们的服务器上放置一个代理页面吗?

          然后通过代理加载脚本。然后,您可以通过多种方式控制两台服务器之间的连接、添加加密等。

          什么是登录系统?使用 SSO 解决方案并将脚本分开会怎样?

          【讨论】:

            【解决方案7】:

            您可以为每个客户端 IP 地址设置散列密钥特定,并使用帖子标题中的 IP 在服务器上比较每个帖子的该值。这样做的好处是,如果有人欺骗他们的 IP响应仍将发送到欺骗的 IP,而不是攻击者的。您可能已经知道这一点,但我也建议在您的哈希中添加盐。

            对于欺骗性 IP,无法进行正确的 TCP 握手,因此攻击者的欺骗性帖子无法完成。

            可能还有其他我不知道的安全问题,但我认为这可能是一种选择

            【讨论】:

            • 对于不同的请求,单个客户端可能看起来来自不同的 IP 地址,具体取决于其 ISP 的配置方式,因此特定于客户端(第一个)IP 的散列密钥稍后可能看起来无效来自另一个 IP。这不是一种常见的情况,但足够常见,以至于您可能会以他们无法真正补救或控制的方式破坏您的一些用户。
            【解决方案8】:

            在每个域的基础上为人们提供密钥。

            让人们在请求中包含 [key string + request parameters] 的值。 (哈希值应该在服务器上计算)

            当他们向你发送请求时,你知道参数和密钥,就可以验证有效性。

            【讨论】:

            • 密钥字符串在 JavaScript 中,所以任何人都可以看到它并使用它来制定虚假的 POST 请求。
            【解决方案9】:

            怎么样:

            站点 A 创建一个随机数(基本上是一个随机字符串),将其发送到您的站点 B,然后将其放入会话中。然后,当站点 A 从站点发出 POST 请求时,它会随请求一起发送随机数,并且仅当随机数与站点 B 会话中的随机数匹配时才接受请求。

            【讨论】:

            • 谢谢,Kai,但我不认为这能解决问题——它只会让系统更难破解(这仍然有一些价值)。恶意方仍然可以伪造此处所需的请求。根本问题是客户端代码没有被隐藏,因此无法保守秘密。一旦你弄清楚浏览器是如何与服务器通信的,你就可以模仿它,而服务器就更聪明了。
            【解决方案10】:

            如果您可以将服务器端代码添加到将数据推送到您的站点的站点,您可以使用 MAC 来至少防止未登录的用户发送任何内容。

            如果只允许任何人使用该页面,那么我想不出一种不刮网页就可以确认数据的防水方式。您可以通过引荐来源检查等让发送任意内容变得更加困难,但并非 100% 不可能。

            【讨论】:

            • 非常感谢您的意见,马蒂。不幸的是,我无法向发布文本内容的站点添加任何服务器端代码。我的感觉似乎与您的一致,即确保这一点在技术上是不可能的,但我希望找到某种方法,至少使攻击变得异常困难并且不值得麻烦。
            • 在这种情况下,MAC 很容易被欺骗。你应该如何对攻击者保密?
            • 在服务器和服务之间使用共享密钥或公钥/私钥。显然,这并不能阻止登录用户滥用或泄露它,只有未经身份验证的人。
            猜你喜欢
            • 1970-01-01
            • 2010-11-19
            • 2018-03-23
            • 2017-06-20
            • 1970-01-01
            • 1970-01-01
            • 2015-10-07
            • 1970-01-01
            相关资源
            最近更新 更多