JAX-RS 安全性和身份验证

Posted

技术标签:

【中文标题】JAX-RS 安全性和身份验证【英文标题】:JAX-RS security and Authentication 【发布时间】:2015-12-18 20:59:53 【问题描述】:

我正在开发 REST webService,我的一些客户会使用我的 webservices,所以为了识别真正的客户,我决定给他们一个唯一的 Application Token 给每个真正的客户。客户端将对这个 Token 进行编码,他们会将这个 Token 放在 Request 标头中,我已经在我的 REST Web 服务中配置了一个 REST 过滤器来验证 Token 。我不想使用 https 。我的问题是任何人都可以从我的客户端站点获取该 Token 并且可以使用我的 REST webservices 。我怎样才能阻止这种情况?

【问题讨论】:

你不使用https的原因是什么? 【参考方案1】:

由于您不想使用 https,因此我认为这里的保密性不是问题,并且您只想根据谁发出请求来授权请求​​。您应该要求您的客户签署他们的请求,而不是传递可能被盗的普通令牌。你在这里有一个很好的解释:

Implementing HMAC authentication for REST API with Spring Security Using HMAC to authenticate Web service requests websec.io - API Authentication

简而言之,取自Implementing HMAC authentication for REST API with Spring Security:

    客户端和服务器共享一个秘密访问密钥和一个公共访问密钥。 客户端创建一个包含三个基本元素的请求:公钥标头(纯文本)、日期标头、使用秘密访问密钥对请求的某些数据进行散列计算得出的签名字符串。该哈希通常包含 http 方法、uri 路径、日期标头的值(用于回复攻击)、请求的所有内容(用于 POST 和 PUT 方法)和内容类型。 客户端向服务器发送请求。 服务器读取公钥头并使用它来检索相应的私有访问密钥。 服务器使用私有访问密钥以与客户端相同的方式计算签名。 服务器检查刚刚计算的签名是否与客户端发送的签名匹配。 (可选)为防止回复攻击,服务器检查日期标头中的值是否在可接受的限制内(通常在 5 到 15 分钟之间,以说明时钟差异)。该值不能被恶意攻击者操纵,因为它被用作签名的一部分。如果有人更改了日期标头,服务器将计算出与客户端计算出的签名不同的签名,因此第 6 步将失败。

这个逻辑可以使用任何编程语言来实现。以下是java中的伪代码签名示例:

//clientId is the public client identifier
//secretKey is the key shared between server and client
//requestContent is a string representation of the HTTP request (HTTP verb, body, etc.

//init signing key and mac
SecretKeySpec signingKey = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey);

//sign the request content
byte[] rawHmac = mac.doFinal(requestContent.getBytes());

//encode to base64
String result = base64(rawHmac);

//store in header
request.setHeader("Authorization", "MyAPI " + clientId + ":" + result);

在服务器端,当您收到该请求时,您从标头中提取 clientId 和签名,检索与收到的 clientId 对应的密钥,重新计算签名(完全如上)并比较结果。它匹配客户端已授权,否则返回 HTTP 403(或任何您想要的错误)。


这样就没有更多的“秘密”可以为中间的潜在人窃取,但是仍然有密钥需要安全地存储在客户端和服务器上。泄露这些密钥会危及整个系统。

【讨论】:

Spring 仅用作示例,可以省略。你可以用任何编程语言来实现这个逻辑。 如果有人复制了步骤 2 中所说的所有三个基本元素,他们可以将这些详细信息放在标头中,然后发送请求,在这种情况下会发生什么?【参考方案2】:

由于令牌无法在 HTTP 层上安全传输,因此可以轻松获取此令牌。您可以要求真正的客户端通过结合一些具有时间戳的逻辑来加密这个令牌,这样每次令牌都使用一些不同的算法进行加密,并且在服务器端您应该遵循类似的算法来解密它。这样即使有人拿到了无法重复使用的令牌。一种方法是将这种加密逻辑与 Google Authenticator 结合使用。 (http://www.techrepublic.com/blog/google-in-the-enterprise/use-google-authenticator-to-securely-login-to-non-google-sites/)

【讨论】:

【参考方案3】:

使用校验和来保护以下消息

MD5 或 SHA1 校验和应用于验证密码而不传递实际密码。

The server sends a random string to the client.
The client appends his password to the random string, and returns an MD5/SHA1 sum of the result to the server.
On the server, do the same and compare the MD5/SHA1 sums.
If both MD5/SHA1 are identicals then the password is good and message is not changed.

【讨论】:

以上是关于JAX-RS 安全性和身份验证的主要内容,如果未能解决你的问题,请参考以下文章

JAX-RS 和 Spring Security - 获取经过身份验证的用户

使用拦截器和注入在 JAX-RS 中进行身份验证/授权

Java:使用带有 Spring Security 的 Jersey Jax-RS 进行用户身份验证

使用 JAX-RS Jersey 进行身份验证和授权的简便方法

使用 JAX-RS Jersey 进行身份验证和授权的简便方法

在 JAX-RS 中使用 API 密钥进行身份验证