The Ins and Outs of Token Authentication

Not long ago, I mentioned token-based authentication in the practice of front -end and back-end separation , and now let's go a little deeper.

Usually, when we discuss a technology, we start with a problem. So the first question:

Why use Token?

And answering that question is easy - because it solves the problem!

What problems can be solved?

  1. Token is completely managed by the application, so it can avoid the same-origin policy
  2. Token can avoid CSRF attacks
  3. Tokens can be stateless and can be shared among multiple services

Token is generated on the server side. If the front-end uses the username/password to request authentication from the server, and the server-side authentication succeeds, the server will return a Token to the front-end. The front-end can bring Token with each request to prove its legal status. If this Token is persistent on the server side (such as stored in a database), it is a permanent identity token.

So, another question arises: Do I need to set an expiration date for the Token?

Do I need to set an expiration date?

For this question, let us look at two examples. An example is the login password, which is generally required to be changed regularly to prevent leakage, so the password has a validity period; another example is a security certificate. SSL security certificates have a validity period, the purpose is to solve the problem of revocation, for the details of this problem, take a look at Zhihu's answer . Therefore, whether from the perspective of security or from the perspective of revocation, the Token needs to have a validity period.

So how long is the validity period appropriate?

It can only be said that according to the security needs of the system, it should be as short as possible, but it should not be too short. Imagine the time for the automatic screen shut-off of the mobile phone. If it is set to automatically shut off the screen after 10 seconds of no operation, you need to enter a password to turn it on again. , will it be crazy? If you don’t think so, try it yourself, set it to the shortest time that can be set, and stick to it for a week (it is not ruled out that some people adapt to this time, after all, mobile phone manufacturers also have user experience research).

Then a new problem arises. If the Token expires during the normal operation of the user, the user is required to log in again... Isn't the user experience very bad?

In order to solve the problem that the user cannot feel that the Token is invalid during the operation, there is a solution to save the Token state on the server side, and the user will automatically refresh (delay) the expiration time of the Token every time the user operates - Session adopts this strategy to keep the user logged in state. However, there is still such a problem. In the case of front-end and back-end separation and single-page App, many requests may be initiated per second, and refreshing the expiration time every time will result in a very high cost. If the Token's expiration time is persisted to the database or file, the cost is even greater. Therefore, in order to improve efficiency and reduce consumption, the Token will be stored in the cache or memory when it expires.

There is another solution, using Refresh Token, which can avoid frequent read and write operations. In this scheme, the server does not need to refresh the expiration time of the Token. Once the Token expires, it will be fed back to the front-end, and the front-end will use the Refresh Token to apply for a new Token for continued use. In this scheme, the server only needs to check the validity of the Refresh Token once when the client requests to update the Token, which greatly reduces the operation of updating the validity period and avoids frequent reading and writing. Of course, the Refresh Token also has a validity period, but the validity period can be longer, for example, in days.

Timing diagram representation

The sequence diagram of using Token and Refresh Token is as follows:

1) Login

2) business request

3) Token expires, refresh Token

The above sequence diagram does not mention what happens when the Refresh Token expires. But obviously, since the Refresh Token has expired, it is time to ask the user to log in again.

Of course, this mechanism can also be designed to be more complicated. For example, every time the Refresh Token is used, its expiration time is updated until it exceeds a very long period of time compared with its creation time (such as three month), which is equivalent to allowing the automatic renewal of the Refresh Token for a considerable period of time.

So far, tokens are all stateful, that is, related attributes need to be saved and recorded on the server side. So what about statelessness, how to achieve it?

Stateless Token

If we attach all the state information to the Token, the server can not save it. But the server still needs to authenticate that the Token is valid. However, as long as the server can confirm that it is the Token issued by itself, and its information has not been changed, it can be considered that the Token is valid - "signature" can make this guarantee. Usually, the signatures that are often mentioned are issued by one party and verified by the other party, so an asymmetric encryption algorithm should be used. But here, both the issuing and the verification are the same party, so the symmetric encryption algorithm can meet the requirements, and the symmetric algorithm is much faster than the asymmetric algorithm (up to tens of times the difference). Thinking further, in addition to encryption, the symmetric encryption algorithm also has the function of restoring the encrypted content, and this function is not necessary when signing the Token - since no decryption is required, the digest (hash) algorithm will be faster. The hash algorithm of the password can be specified, which is naturally HMAC.

So much has been said above, do you still need to implement it yourself? Need not! JWT has a detailed specification defined, and there are several implementations in various languages.

However, when using stateless Token, there will be some changes on the server side. Although the server side does not save the valid Token, it needs to save the Token that has not expired but has been cancelled. If a Token is actively cancelled by the user before it expires, the server needs to save the cancelled Token, so that the next time it receives and uses the token that is still valid, it will be invalid. Ever feel a little frustrated?

When the front-end is controllable (for example, the front-end and the server are in the same project group), it can be negotiated: once the front-end is successfully logged out, the locally saved Token and Refresh Token (such as stored in memory, LocalStorage, etc.) will be discarded. Based on this agreement, the server can assume that the received Token must not be cancelled (because the front end will not be used again after the cancellation).

If the front end is uncontrollable, the above assumptions can still be made, but in this case, the validity period of the Token needs to be shortened as much as possible, and the Refresh Token must be invalidated when the user actively logs out. This operation has certain security loopholes , because the user will think that he has logged out, but in fact, he has not logged out in a short period of time. If the application design, this vulnerability does not cause any damage, then adopting this strategy is feasible.

When using stateless tokens, there are two points to note:

  1. The Refresh Token is valid for a long time, so it should be stateful on the server side to enhance security and ensure controllable user logout
  2. Two-factor authentication should be considered to enhance the security of sensitive operations

At this point, the topic of Token seems to be almost the same - but it is not. The above is only the case where the authentication service and the business service are integrated. What if it is a separate case?

Separate Authentication Service

When the token is stateless, single sign-on becomes easy. Once the front end gets a valid Token, it can be authenticated on any service of the same system - as long as they use the same key and algorithm to authenticate the validity of the Token. Just like this:

Of course, if the Token expires, the front end still needs to go to the authentication service to update the Token:

It can be seen that although authentication and business are separated, there is not much difference in practice. Of course, this is based on the premise that the authentication server trusts the service server , because the key and algorithm used by the authentication server to generate the Token are the same as the key and algorithm used by the service server to authenticate the Token. In other words, the business server can also create a valid Token.

What if the business server cannot be trusted?

Untrusted business server

When encountering an untrusted business server, it is easy to think of a different key. The authentication server uses key 1 to issue, and the business server uses key 2 to verify—this is a typical application scenario of asymmetric cryptographic signatures. The authentication server uses the private key to sign the Token and discloses the public key. The business server that trusts this authentication server saves the public key, which is used to verify the signature. Fortunately, JWTs can be signed not only with HMAC, but also with RSA, an asymmetric encryption algorithm.

However, when the business server is already untrusted, it is not safe for users to use the same Token among multiple business servers . Because any server that gets the Token can impersonate the user to go to another server to process business... Tragedy can happen at any time.

In order to prevent this from happening, it is necessary to record the information of the business server using the Token in the Token when the authentication server generates the Token, so that when another business server gets the Token, it finds that it is not what it should be. The verified Token can be rejected directly.

Now, the authentication server doesn't trust the business server, and the business servers don't trust each other, but the front end trusts these servers - if the front end doesn't trust it, it won't take the Token to request verification. So why trust? It may be because these are service systems composed of several services provided by the same company or in the same project.

However, front-end trust does not imply user trust. If the Token does not carry user privacy (such as name), then the user will not care about trust issues. But if the Token contains user privacy, users have to care about trust issues. At this time, the authentication service has to be more verbose. When the user requests a Token, ask the last question, do you really want to authorize a certain business service? And this "so-and-so", how does the user know if it is really "so-and-so"? Of course users don't know, not even the authentication service, because the public key has been made public, any business can declare itself as "so-and-so".

In order to gain the trust of users, authentication services have to help users to identify business services. Therefore, the authentication server decides not to disclose the public key, but requires the business service to apply for registration and pass the audit first. Only the business server that passes the audit can get the public key created by the authentication service for it and only used by it. If the business service leaks the public key, the risk is borne by the business service. Now the authentication service can clearly tell the user what the "so-and-so" service is. If the user is still not trustworthy enough, the authentication service can even ask, XX business service needs to request three personal data of A, B, and C, of ​​which A is required, otherwise it will not work, is authorization allowed? If you authorize, I will encrypt several items of data you authorized and put them in the Token...

So much nonsense, does it seem familiar... By the way, this is similar to the authentication process of an open API. Development APIs mostly use OAuth authentication, and the resources for discussing OAuth are very rich, so I won't go into details here.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324375518&siteId=291194637