拥有公共 API 但只允许访问从我的网站发送的请求

IT技术 javascript node.js rest security reactjs
2021-05-16 16:06:09

我已经在 Google 和 SO 上搜索了几个小时,但没有找到与我现在面临的挑战相同的人,所以这里是:

我们有一个数据库,我们投入了大量金钱和精力来维护它。数据库中的数据可通过 REST-API 公开获得。我们还有一个使用此 API 的公共 javascript 网络应用程序,我们将其出售给大约 30-40 个客户。由于 API 中的数据对我们来说非常有value,我们希望尝试保护它,以便没有人可以从中抓取内容并制作自己的数据库副本。我们也不希望任何人在未经我们同意的情况下使用我们的 API 构建服务。同时,我们需要我们在http://www.example.com/theApphttp://www.example2.com/theApphttp://www.example3.com/theApp等的网络应用程序才能访问 API。没有用户参与。大家可以去http://www.example.com/theApp并获得网站的全部功能。API 也是只读的,因此我们不会担心任何试图污染我们数据的人。

javascript web 应用程序是使用 react.js 和 node.js 服务器构建的。当然,SSL 将用于服务器和客户端之间的所有通信。

我认为行不通的事情:

  • 使用户登录以使用http://www.example.com/theApp(没有额外的登录功能,所以这只会惹恼用户。)
  • 在 Web 应用程序中存储密码/令牌。(javascript 中没有任何东西是安全的,但混淆代码是安全的。)
  • 使 Web 应用程序客户端从身份验证服务器请求令牌。(这个请求也可以由敌对的客户端完成。)
  • 将允许的 IP 列入白名单。(每个人都可以访问这些网站...)
  • 将 URL 列入白名单。(URL 作为标头发送。标头可以被操纵。)

可能有效的事情(或至少是解决方案的一部分):

  • 让 node.js 服务器应用程序代表客户端从身份验证服务器请求令牌。(但我仍然不知道这将如何防止恶意客户端向 node.js 服务器应用程序发出相同的请求)
  • 混淆视听。(这可能会让大多数敌对用户望而却步,但更持久(和最熟练/最危险?)只会对挑战感到兴奋,并最终能够破解它。)

由于这是我们试图实现的相当复杂的事情,所以我开始相信这可能是不可能的,如果有人对该怎么做有一些建议,我将不胜感激。如果给出充分的理由,“不要这样做”是一个非常好的答案。我在这里更多的是概念性解决方案,但如果有人想具体了解软件,我们有一个带有 node.js、Nginx 和 PHP 的 Linux 环境。

3个回答

如果您不想对您的用户进行身份验证(您不希望他们登录),您将无法知道谁在使用您的 API,或者哪些请求来自哪个用户。发出 API 请求所需的所有信息都已在 javascript 客户端中,任何人都可以创建另一个客户端,或向 API 发出有效请求,您甚至无法判断该请求是来自新客户端还是与以前的请求(想要下载您的数据库的人可以在许多客户端计算机上分发下载)。

Daniel 的回答可能是您通过这种方式最接近目标的答案。如果您发行代币,则至少可以撤销这些代币。但是,您仍然无法阻止攻击者请求新的攻击者。

阅读您的问题后,我的想法是 javascript Web 应用程序可能不是您想要的。您是说您的客户相对较少,而且您根本不想登录。是否可以选择为他们提供桌面/移动客户端之类的东西?它仍然可以是封装在某种容器中的 Javascript,但是对于每个客户端,您可以编译一个包含自己的密钥。一个明显的风险是他们仍然可以提取密钥,但这样一来,不是任何人都可以拥有拥有密钥的客户,事实上,泄漏的密钥将与其合法所有者相关联,并且您可以有合同条款来涵盖这种情况(禁止逆向工程等)。您还可以监视和控制数据库的大量下载并撤销违规密钥,

当然,这种风险在您的情况下可能会或可能不会被接受,只是一个想法。显然,从客户端获取密钥很容易,关键是不是每个人都有客户端可以从中获取有效密钥。更好的方法可能是将密钥作为客户端的许可证文件单独分发,它本质上是相同的,但没有“硬编码密钥”的概念(我认为在这种情况下它们不是真的)。这将使您的生活更轻松,因为您不需要在密钥被撤销的情况下分发完整的客户端,只需分发一个新的许可证文件。

这当然是身份验证,但在您的情况下可能对用户更友好,因为用户不需要做任何事情。

对于该数量的客户端的另一个想法是客户端证书。对于想要使用您的 API 的任何客户端,您将提供一个客户端证书。任何人都可以下载您的 Web 应用程序,但客户端证书可用于对 API 的调用者进行身份验证。它与上面的键相同,但处于不同的级别。撤销和发布一个新数据库更困难(并且可能更昂贵),您仍然无法阻止下载整个数据库,但同样,您将有证据证明是谁这样做的,并且可以撤销他们的密钥并将其包含在您的客户端中合同。

在所描述的场景中,混淆是您唯一的选择。您想让攻击者很难对您的 Web 客户端进行逆向工程。

如果客户端需要进行复杂的计算才能成功发出 API 请求,则攻击者必须对该计算进行逆向工程并将其复制到另一个应用程序中。

一些有帮助的事情:

  • 缩小的 JavaScript
  • 从服务器收到的令牌(例如 CSRF 令牌),然后您在 Web 客户端代码中以某种方式对其进行散列或转换
  • 在计算中的某处包括当前日期

以上都不会阻止一个有足够动机的攻击者。

在我看来,最安全的解决方案是oauth2或者如果您想要更大的灵活性oauth

它允许生成和终止访问令牌。您可以计算任何令牌获取了多少资源,并在有人试图下载您的所有数据时使其无效。

很棒的教程:

https://oauth2.thephpleague.com/