本文介绍了基于 REST API 令牌的身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发需要身份验证的 REST API.因为身份验证本身是通过 HTTP 上的外部 Web 服务进行的,所以我推断我们会分配令牌以避免重复调用身份验证服务.这让我巧妙地回答了我的第一个问题:

I'm developing a REST API that requires authentication. Because the authentication itself occurs via an external webservice over HTTP, I reasoned that we would dispense tokens to avoid repeatedly calling the authentication service. Which brings me neatly to my first question:

这真的比只要求客户端对每个请求使用 HTTP 基本身份验证和缓存对身份验证服务服务器端的调用更好吗?

Basic Auth 解决方案的优势在于,在开始对内容的请求之前不需要到服务器的完整往返.令牌的范围可能更灵活(即仅授予对特定资源或操作的权限),但这似乎比我更简单的用例更适合 OAuth 上下文.

The Basic Auth solution has the advantage of not requiring a full round-trip to the server before requests for content can begin. Tokens can potentially be more flexible in scope (i.e. only grant rights to particular resources or actions), but that seems more appropriate to the OAuth context than my simpler use case.

目前代币的获取方式如下:

Currently tokens are acquired like this:

curl -X POST localhost/token --data "api_key=81169d80...
                                     &verifier=2f5ae51a...
                                     &timestamp=1234567
                                     &user=foo
                                     &pass=bar"

api_keytimestampverifier 是所有请求所必需的.验证者"返回:

The api_key, timestamp and verifier are required by all requests. The "verifier" is returned by:

sha1(timestamp + api_key + shared_secret)

我的目的是只允许来自已知方的呼叫,并防止呼叫被逐字重复使用.

My intention is to only allow calls from known parties, and to prevent calls from being reused verbatim.

这样够好吗?杀伤力不足?矫枉过正?

持有令牌,客户可以获取资源:

With a token in hand, clients can acquire resources:

curl localhost/posts?api_key=81169d80...
                    &verifier=81169d80...
                    &token=9fUyas64...
                    &timestamp=1234567

对于最简单的调用来说,这似乎有点冗长.考虑到 shared_secret 最终会被嵌入到(至少)一个 iOS 应用程序中,我认为它可以从中提取出来,这是否提供了超出虚假安全感的任何东西?

For the simplest call possible, this seems kind of horribly verbose. Considering the shared_secret will wind up being embedded in (at minimum) an iOS application, from which I would assume it can be extracted, is this even offering anything beyond a false sense of security?

推荐答案

让我把一切都分开,单独解决每个问题:

Let me seperate up everything and solve approach each problem in isolation:

身份验证

对于身份验证,baseauth 的优势在于它是协议级别的成熟解决方案.这意味着很多稍后可能会出现" 的问题已经为您解决了.例如,使用 BaseAuth,用户代理知道密码是一个密码,因此他们不会缓存它.

For authentication, baseauth has the advantage that it is a mature solution on the protocol level. This means a lot of "might crop up later" problems are already solved for you. For example, with BaseAuth, user agents know the password is a password so they don't cache it.

身份验证服务器负载

如果您向用户分配令牌而不是在您的服务器上缓存身份验证,您仍然在做同样的事情:缓存身份验证信息.唯一的区别是您将缓存的责任交给了用户.这对用户来说似乎是不必要的劳动,没有任何收益,因此我建议按照您的建议在您的服务器上透明地处理此问题.

If you dispense a token to the user instead of caching the authentication on your server, you are still doing the same thing: Caching authentication information. The only difference is that you are turning the responsibility for the caching to the user. This seems like unnecessary labor for the user with no gains, so I recommend to handle this transparently on your server as you suggested.

传输安全

如果可以使用 SSL 连接,仅此而已,连接就是安全的*.为防止意外多次执行,您可以过滤多个网址或要求用户在网址中包含随机组件(nonce").

If can use an SSL connection, that's all there is to it, the connection is secure*. To prevent accidental multiple execution, you can filter multiple urls or ask users to include a random component ("nonce") in the URL.

url = username:[email protected]/api/call/nonce

如果这是不可能的,并且传输的信息不是秘密的,我建议使用散列来保护请求,正如您在令牌方法中所建议的那样.由于散列提供了安全性,您可以指示您的用户提供散列作为 baseauth 密码.为了提高健壮性,我建议使用随机字符串而不是时间戳作为随机数"以防止重放攻击(可以在同一秒内发出两个合法请求).无需提供单独的共享密钥"和api 密钥"字段,您可以简单地使用 api 密钥作为共享密钥,然后使用不会改变的盐来防止彩虹表攻击.用户名字段似乎也是放置随机数的好地方,因为它是身份验证的一部分.所以现在你有一个像这样干净的调用:

If that is not possible, and the transmitted information is not secret, I recommend securing the request with a hash, as you suggested in the token approach. Since the hash provides the security, you could instruct your users to provide the hash as the baseauth password. For improved robustness, I recommend using a random string instead of the timestamp as a "nonce" to prevent replay attacks (two legit requests could be made during the same second). Instead of providing seperate "shared secret" and "api key" fields, you can simply use the api key as shared secret, and then use a salt that doesn't change to prevent rainbow table attacks. The username field seems like a good place to put the nonce too, since it is part of the auth. So now you have a clean call like this:

nonce = generate_secure_password(length: 16);
one_time_key = nonce + '-' + sha1(nonce+salt+shared_key);
url = username:[email protected]/api/call

的确,这有点费力.这是因为您没有使用协议级解决方案(如 SSL).因此,向用户提供某种 SDK 可能是一个好主意,这样至少他们不必自己通过它.如果您需要这样做,我认为安全级别合适(just-right-kill).

It is true that this is a bit laborious. This is because you aren't using a protocol level solution (like SSL). So it might be a good idea to provide some kind of SDK to users so at least they don't have to go through it themselves. If you need to do it this way, I find the security level appropriate (just-right-kill).

安全的秘密存储

这取决于你想阻止谁.如果您阻止有权访问用户手机的人以用户的名义使用您的 REST 服务,那么最好在目标操作系统上找到某种密钥环 API 并让 SDK(或实施者)存储关键在那里.如果这是不可能的,您至少可以通过对其进行加密并将加密数据和加密密钥存储在不同的地方来使获取机密变得更难一些.

It depends who you are trying to thwart. If you are preventing people with access to the user's phone from using your REST service in the user's name, then it would be a good idea to find some kind of keyring API on the target OS and have the SDK (or the implementor) store the key there. If that's not possible, you can at least make it a bit harder to get the secret by encrypting it, and storing the encrypted data and the encryption key in seperate places.

如果您试图阻止其他软件供应商获取您的 API 密钥以阻止备用客户端的开发,那么只有加密并单独存储的方法几乎有效.这是白盒加密,迄今为止,没有人为此类问题提出真正安全的解决方案.您至少可以为每个用户分配一个密钥,以便您可以禁止滥用密钥.

If you are trying to keep other software vendors from getting your API key to prevent the development of alternate clients, only the encrypt-and-store-seperately approach almost works. This is whitebox crypto, and to date, no one has come up with a truly secure solution to problems of this class. The least you can do is still issue a single key for each user so you can ban abused keys.

(*) SSL 连接不应再被视为安全无需采取额外步骤来验证.

这篇关于基于 REST API 令牌的身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-20 21:27