令牌桶 客户端请求_使用刷新的令牌和cookie进行安全的客户端身份验证

令牌桶 客户端请求

To start off, this is my first article here. I’ve thought about writing it for a while now. Ever since we planned and implemented what we call a moderately secure way to actually authenticate client-side requests against our API where authentication tokens are refreshed and old ones are rapidly expired.

要开始了,这是我在这里的第一篇文章。 我已经考虑过写一段时间了。 自从我们计划并实施了所谓的中度安全方式,即根据我们的API对客户端请求进行实际身份验证,在该API中,身份验证令牌会更新,而旧令牌则会快速过期。

I thought I’d share the idea. I will not be diving into much detail here by using big code examples.

我以为我会分享这个想法。 在这里,我不会通过使用大型代码示例来详细介绍。

LocalStorage与Cookie (LocalStorage vs. Cookies)

We use JSON Web Tokens as authentication tokens. Tokens are generated when the user logs in with his credentials and contains information such as his username (or id). More about the payload below.

我们使用JSON Web令牌作为身份验证令牌。 当用户使用其凭据登录时会生成令牌,其中包含诸如用户名(或ID)之类的信息。 有关以下有效负载的更多信息。

This token needs to be stored somewhere on the client side. After doing some research, we came to the conclusion that LocalStorage is not secure as a token storage. As it is a client-side, persistent storage (does not get cleared unless programmatically told to or when the user clears their browser cache), it can be accessed by other sites as well. So it was a no-go to store tokens.

该令牌需要存储在客户端的某个位置。 经过研究,我们得出的结论是LocalStorage作为令牌存储并不安全 。 由于它是客户端的持久性存储(除非以编程方式告知用户或当用户清除其浏览器缓存时,否则不会清除它),其他站点也可以访问它。 因此,存储令牌是不可行的。

Cookies are considered to be more secure. But only if they contain the proper flags. So of course for us this meant HttpOnly, Secure etc. However, JavaScript does not have an API for HttpOnly cookies. They are not accessible to scripting languages.

Cookies被认为更安全。 但前提是它们包含正确的标志。 因此,对我们来说,这当然意味着HttpOnly,Secure等。但是,JavaScript没有用于HttpOnly cookie的API。 脚本语言无法访问它们。

So what does this mean? It meant that our API had to handle the authentication cookie.

那么这是什么意思? 这意味着我们的API必须处理身份验证Cookie。

用于处理HttpOnly cookie的API (API to handle HttpOnly cookies)

This proved to be great for us. It meant that our front-end does not have to handle anything cookie related. Why did we not think of this from the start?

事实证明,这对我们来说很棒。 这意味着我们的前端不必处理任何与c​​ookie相关的事情。 为什么我们从一开始就没有想到这一点?

The client just has to call an endpoint with the username and password, and if it gets a successful response (200 OK), you know the cookie exists and your subsequent requests can be authenticated.

客户端只需要使用用户名和密码来调用端点,并且如果它获得成功的响应(200 OK),您就知道该cookie存在并且可以对您的后续请求进行身份验证。

So the cookie has to be set in the API’s response.

因此必须在API的response中设置cookie。

使用PSR-7响应的响应cookie的简单PHP示例: (Simple PHP example of a response cookie using PSR-7 response:)

We use PHP as our back-end language and Slim Framework for our REST API. To handle cookies, we installed FIG Cookies as PSR-7 compliant cookie manager. A cookie inserted in the response, from your controller, looks like this:

我们使用PHP作为后端语言,并使用Slim Framework作为REST API。 为了处理cookie,我们将FIG Cookies安装为符合PSR-7的cookie管理器。 来自控制器的响应中插入的Cookie如下所示:

$response = FigResponseCookies::set($response,
SetCookie::create('token')
->withValue($jwt) # Authentication token
->withDomain('example.com')
->withPath('/')
->withExpires(time() + (60 * 45))
->withSecure(true)
->withHttpOnly(true)
->withSameSite(SameSite::strict())
);

So, when the client makes a POST call to /user/login, we first check if the username and password are valid and if they are, we return a response with the cookie in it. Very simple.

因此,当客户端对/user/login进行POST调用时,我们首先检查用户名和密码是否有效,如果有效,我们将返回其中包含cookie的响应。 很简单。

JSON Web令牌 (JSON Web Tokens)

Now, the cookie contains a token. I won’t go into detail about JWT’s. What I will explain, though, is that they contain a payload with information such as your username, and an expiration time. The payload is in JSON format.

现在,cookie包含一个令牌。 我不会详细介绍JWT。 不过,我将解释的是它们包含一个有效负载,其中包含诸如用户名和到期时间之类的信息。 有效负载为JSON格式。

{
"username": "john.doe@example.com"
"exp": 1516239022
}

有效载荷 (Payload)

Now, your payload is publicly readable. You can test tokens at jwt.io if you want. Just paste the generated token there and read it’s payload. Because to this, you cannot store sensitive data inside your payload! What we have there is just the username and expiration time.

现在,您的有效载荷是公开可读的。 您可以根据需要在jwt.io上测试令牌。 只需将生成的令牌粘贴到此处并读取其有效载荷即可。 因此,您不能在有效负载中存储敏感数据! 我们所拥有的只是用户名和有效时间。

期满 (Expiration)

We set our token (not just cookie) expiration to 45 minutes. Enough for you to log in, maybe grab lunch, get back to using the app. Some people consider this to be too long. To an attacker, who somehow gains access to your token, this is enough time to actually log in imposing as you and start reading your data. I will return to this further down.

我们将令牌 (不仅仅是cookie)的有效期设置为45分钟。 您已经足够登录,也许可以享用午餐,然后重新使用该应用程序。 有人认为这太长了。 对于以某种方式可以访问您的令牌的攻击者来说,这是足够的时间来实际登录强加给您并开始读取数据。 我将进一步讨论这一点。

刷新令牌 (Refreshing tokens)

So the token is only valid for 45 minutes there. After that the user gets logged out. That’s not gonna work. We need to refresh the token with each valid request (middleware does the signature validation).

因此令牌仅在此处有效45分钟。 之后,用户将注销。 那是行不通的。 我们需要用每个有效的请求刷新令牌(中间件执行签名验证)。

We created an afterware to refresh the token. This afterware gets the token from the request, modifies the token by taking its payload, refreshing the expiration property, and returning a new token in the response, inside the same cookie. So now you have a new, refreshed, token with each request you make.

我们创建了一个固件来刷新令牌。 此后件从请求中获取令牌,通过获取其有效载荷,刷新expiration属性并在响应中的同一cookie内返回新令牌来修改令牌。 因此,现在您对每个请求都有一个新的,刷新的令牌。

Here is an example of such an afterware:

这是一个此类固件的示例:

<?php
namespace App\Middleware;
use Dflydev\FigCookies\FigResponseCookies;
use Dflydev\FigCookies\SetCookie;
use Firebase\JWT\JWT;
use Slim\Http\Request;
use Slim\Http\Response;
final class RefreshTokenExpirationMiddleware {
public function __invoke (Request $request, Response $response, $next) {
# Rewind to get response
$response = $next($request, $response);
$response->getBody()->rewind();
# Pick up the current token (already decoded by the middleware)
$token = $request->getAttribute('auth_token');
if($token) {
# Add 45 minutes to token
$token['exp'] = time() + (60 * 45);
# New token
$encoded = JWT::encode($token, 'secret', ['HS256']);
$modify = function(SetCookie $setCookie) use ($encoded) {
return $setCookie->withValue($encoded)->withExpires(time() + (60 * 45));
};
$response = FigResponseCookies::modify($response, 'auth_token', $modify);
}
return $response;
}
}

将令牌列入白名单 (Whitelisting tokens)

So returning to the expiration; 45 minutes is a big gap. And we are creating more and more tokens with each request! This creates a nice little pile of valid tokens a possible attacker can grab and use. It’s a problem.

回到到期日; 45分钟是一个很大的差距。 而且,每个请求都将创建越来越多的令牌! 这会产生一小堆有效的令牌,可能的攻击者可以抓住并使用。 这是一个问题。

So what we did is we found a nice idea online of a blacklist. However, we made it a whitelist.

因此,我们所做的就是在网上找到了一个黑名单的好主意。 但是,我们将其列入了白名单。

When the user logs in, and the first token is created, that token gets placed inside a whitelist container that resides in Redis with a TTL (Time to Live) of 45 minutes. A simple storage, but with automatic expiration.

当用户登录并创建第一个令牌时,该令牌将放入白名单容器中 ,该容器位于Redis中 ,其TTL (生存时间)为45分钟。 一个简单的存储,但具有自动到期时间。

We already had one middleware in place to check if the token’s signature is valid. We then created another middleware that checks if the token is still inside the whitelist. (Sorry, no code examples here, just an explanation).

我们已经有一个中间件来检查令牌的签名是否有效。 然后,我们创建了另一个中间件,用于检查令牌是否仍在白名单中。 (对不起,这里没有代码示例,只是一个解释)。

If the signature is OK and the token is still whitelisted, the request moves forward. Otherwise you get a 401 Unauthorized response.

如果签名确定,并且令牌仍被列入白名单,则请求将继续进行。 否则,您将收到401 Unauthorized回复。

Now, after the request has been completed the afterware mentioned earlier takes the token you just made the request with, and rapidly expires it. It gets deleted from the whitelist and then put back in with a 5 min TTL. The newly refreshed token gets inserted there with a 45 min TTL.

现在,在请求完成后,前面提到的固件将使用您刚刚发出请求的令牌,并Swift使该令牌过期。 它会从白名单中删除,然后以5分钟的TTL恢复。 新刷新的令牌将以45分钟的TTL插入那里。

With this, you have a valid token with 45 min expiration and any older ones with a 5 min expiration. You just made the window of a possible attack that much more narrow.

这样,您将拥有一个有效的令牌,其有效期为45分钟,而任何较旧的令牌的有效期为5分钟。 您只是将可能的攻击范围缩小了很多。

Image for post
Authentication flow diagram
认证流程图

其他安全措施 (Additional security measures)

So now, you might be thinking that the 45 minutes is still an open window there. An additional security measure would be to actually log the IP-addresses of each token request and check that the requests originate from the same address subsequently. If a mismatch is detected between requests the user is signed out and forced to log back in.

所以现在,您可能会认为45分钟仍然是一个打开的窗口。 另一安全措施是实际记录每个令牌请求的IP地址,并随后检查请求是否源自同一地址。 如果在请求之间检测到不匹配,则用户将注销,并被迫重新登录。

You can maybe do the same for other information as well. You ever seen those warnings Google sends you when they detect you logged in from a new IP-address or machine? There’s an idea.

对于其他信息,您也可以执行相同的操作。 您曾经看过Google向您发送的那些警告,因为它们检测到您是从新IP地址或计算机登录的吗? 有个主意

翻译自: https://medium.com/@rcls/secure-client-side-authentication-using-refreshed-tokens-and-cookies-4dacc0bc8ff0

令牌桶 客户端请求

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值