使用客户端令牌保护 REST API 调用

Posted

技术标签:

【中文标题】使用客户端令牌保护 REST API 调用【英文标题】:Securing REST API calls with client-side token 【发布时间】:2017-04-20 20:02:08 【问题描述】:

我有一个 node.js REST API,我想将 POST/PUT/DELETE 调用限制为预定义的“源”列表(我不拥有代码的 Web 应用程序)。

我认为实现此目的的唯一方法是在客户端放置一个 令牌(类似于 JS 文件中的 Google Analytics),但我不知道如何保护它,因为令牌会可以在静态文件中访问。

我应该使用什么策略? JWT 和 OAuth2 似乎没有指明,因为它需要第一个用户身份验证,但我要验证的不是用户而是 webapps。

【问题讨论】:

【参考方案1】:

你的问题有点不清楚。您可能意味着 (a) 您希望强烈鼓励用户使用该应用程序并防止其他代码恶意使您的用户执行操作,或者 (b) 您希望绝对阻止您的用户使用其他代码访问你的服务器。

第一个选项是可能的,而且确实是一个非常好的主意。第二个是不可能的,基于互联网的运作方式。

首先,不可能。从本质上讲,客户端代码的存在是为了让您的客户生活更轻松。真正的工作将始终在服务器端完成——即使这仅意味着验证数据并将其存储在数据库中。您的客户端将始终能够看到他们发送的所有 HTTP 请求:这就是 HTTP 的工作方式。您无法向他们隐藏信息。即使您动态生成令牌(见下文),您也无法阻止它们在其他地方使用它们。他们总是可以构建一个自定义的 HTTP 请求,这意味着他们最终可以,如果他们真的,真的想要,完全放弃你的应用程序。将您的客户端代码想象成只是让他们更容易执行 HTTP 请求并放弃任何防止他们“做错事”的想法!

更好的选择是 CSRF 保护,它可以为您的服务器和客户端提供最好的保护。这意味着当他们第一次登录时向您的客户发送一个生成的令牌,并在他们在每个请求上发送它时验证它(通过查找或解密它)。这是 JWT 的基础,它是一个相当古老的验证系统的漂亮实现。

【讨论】:

【参考方案2】:

最终,您的 API 是公开的,因为任何随机的网站访问者都必须能够与 API 进行交互。即使您使用令牌来限制访问,根据定义,这些令牌也必须是公开的。即使定期过期和更新令牌(例如通过后端 API 或通过包含 nonce 算法)也无济于事,因为这些新令牌将再次在第三方网站上公开可见,任何人都可以获取。

CSRF 保护可以一点帮助避免浏览器内的跨站点滥用,但最终对于阻止某人编写 API 抓取工具之类的目的毫无意义。

您能做的最好的事情是使用令牌来识别您授予访问权限的各个网站所有者,警惕地监控您的 API 使用,当您认为令牌被滥用时使令牌无效,并联系网站所有者以以某种方式更好地保护他们的令牌(他们会遇到同样的问题,但至少你有责任咳咳)。

【讨论】:

【参考方案3】:

您可以使用 hmac 来保护它: 每个客户端都有一对独特的公钥/私钥(例如“public”和“private”)。

当客户端发送请求时,他必须发送一个随机数+他的用户公钥+随机数的hmac+公钥和他的私钥。

服务器处理请求时,服务器根据客户端的公钥检索客户端,获取用户的秘钥,然后验证签名。

Client, sample call on /api

var nonce = "randomstring";
var pk    = "aaa";
var sk    = "bbb";

var string = "pk="+pk+"&nonce="+nonce;

var crypto = require('crypto');
var hmac   = crypto.createHmac('sha512', sk).update(string).digest('hex');

// send this payload in your request in get, put, post, ....
var payload = string+"&hmac="+hmac; 

request.post(uri:"website.com/api?"+payload, ....

还有

Server side, security check

var nonce = req.query.nonce;
var pk    = req.query.pk;
var hmac  = req.query.hmac;

// retrieve user and his sk according to pk
var sk = getUser(pk).sk

// rebuild payload string
var string = "pk="+pk+"&nonce="+nonce;

var crypto = require('crypto');
var hmac_check   = crypto.createHmac('sha512', sk).update(string).digest('hex');

if(hmac_check === hmac)  // request valid else // invalid request 

【讨论】:

由于所有这些信息都必须嵌入到客户端 javascript 中,是什么阻止我编写重新创建此算法的刮板?这不是很难…… 由于所有信息都嵌入在客户端 Javascriptt 中,因此无法防止报废。可以使用浏览器完成的所有操作也可以使用抓取工具完成。

以上是关于使用客户端令牌保护 REST API 调用的主要内容,如果未能解决你的问题,请参考以下文章

需要使用 Keycloak 和 oAuth2 保护普通 Spring REST API 的示例

保护REST API以供Android客户端使用

如何将接收到的(承载)访问令牌传递给生成的 REST 客户端,以调用安全的 API-Gateway 端点

使用 Spring Security 保护对 Spring MVC REST API 的 jQuery 调用

REST API 中的 CSRF 令牌

REST API访问控制从访问令牌和路径参数中提取主题