Cookie, Session, token, JWT, SSO

Start with status

“HTTP Stateless”

We know that HTTP is stateless. In other words, the HTTP requester and responder cannot maintain state, it is all one-time, and it does not know what happened in the previous and subsequent requests.

But in some scenarios, we need to maintain state. The most typical example is that when a user logs into Weibo, he or she can post, follow, and comment in the logged-in user state.

Cornerstone: cookie

But the front-end is so troublesome. You have to save it yourself and find a way to take it out. Is there anything you don’t have to worry about?

Yes, cookies.

Cookies are also a type of front-end storage, but compared to other methods such as localStorage, cookies can be front-end unaware with the help of HTTP headers and browser capabilities.

The general process is this:

  • In the interface that provides markup, it is directly “seed” to the browser through the Set-Cookie field of the HTTP return header.
  • When the browser initiates a request, it will automatically bring the cookie to the interface through the Cookie field of the HTTP request header.

Application solution: server session

Now think about it, what happened when you swiped your card?

In fact, there is only one ID stored on your card (maybe your student number). When you swipe, the property management system will check your information and account, and then decide “whether you can enter this door” and “which account should the chicken drumstick be deducted from?” ”.

This operation is called session in the front-end and back-end authentication systems.

Typical session login/verification process:

  • The browser logs in and sends the account password. The server checks the user database and verifies the user.
  • The server saves the user login status as Session and generates a sessionId.
  • Return through the login interface and set the sessionId to the cookie
  • After that, the browser requests the business interface again, and the sessionId is brought along with the cookie.
  • The server checks the sessionId to verify the session.
  • After success, perform business processing normally and return results.

“How sessions are stored”

Obviously, the server only gives the cookie a sessionId, and the specific content of the session (which may include user information, session status, etc.) needs to be saved by itself. There are several ways to store:

  • Redis (recommended): In-memory database, redis Chinese official website. It is stored in the form of key-value, which is suitable for the scenario of sessionId-sessionData; and the access is fast.
  • Memory: put directly into variables. Once the service is restarted it will be gone
  • Database: ordinary database. Performance is not high.

“Session Distribution Problem”

Usually the server is a cluster, and user requests will go through load balancing once, not necessarily which machine. Then once the machine requested by the user’s subsequent interface is inconsistent with the machine he requested for login, or the machine requested for login is down, won’t the session become invalid?

There are currently several solutions to this problem.

  • The first is to store sessions centrally from a “storage” perspective. If we use an independent Redis or ordinary database, we can store all sessions in a library.
  • The second is from the perspective of “distribution”, so that requests with the same IP are sent to the same machine during load balancing. Taking nginx as an example, you can configure ip_hash to achieve this.

But the first method is usually adopted, because the second method is equivalent to emasculating load balancing, and still does not solve the problem of “the machine requested by the user is down.”

The difference between Cookie and Session

  • Security: Session is more secure than Cookie. Session is stored on the server side, while Cookie is stored on the client side.
  • Different types of access values: Cookie only supports storing string data. If you want to set other types of data, you need to convert it into a string. Session can store any data type.
  • Different validity periods: Cookies can be set to be kept for a long time. For example, the default login function we often use, Session generally has a short expiration time, and will expire when the client is closed (by default) or the Session times out.
  • Different storage sizes: A single cookie cannot store more than 4K data. Session can store much more data than cookies. However, when there are too many visits, it will occupy too many server resources.

Application solution: token

The maintenance of session causes a lot of trouble to the server. We must find a place to store it, consider distribution issues, and even enable a separate Redis cluster for it. Is there a better way?

I thought about school again. Before there was campus card technology, we all relied on “student ID cards”. The guard guy directly compared me with the face on the student ID card to confirm the validity period, grade and other information on the student ID card, and then he was released.

Looking back and thinking about it, in a login scenario, there is no need to store too many things in the session, so why not package it directly into a cookie? In this way, the server does not need to save it. It only needs to verify the validity of the “certificate” carried by the cookie every time, and it can also carry some lightweight information.

This method is often called token.

The token process is as follows:

  • User logs in, the server verifies the account password and obtains user information
  • Encode user information and token configuration into tokens, and send them to the browser through cookie set
  • After that, the user requests the business interface and carries the token through the cookie.
  • The interface verifies the validity of the token and performs normal business interface processing.

“How client token is stored”

As mentioned before, cookies are not the only way for clients to store credentials. Because token is “stateless”, the validity period and usage restrictions are all included in the token content. It relies less on cookie management capabilities, and the client can save it more freely. But the mainstream method of web applications is still to put it in cookies, after all, there is less to worry about.

“Token Expiration”

So how do we control the validity period of the token? It’s very simple. Just put the “expiration time” and the data in it and judge it during verification.

Token encoding

The way of coding is up to the people.

“base64”

For example, cookie-session on the node side – npm library

Don’t worry about the name. It’s actually a token library, but it maintains a highly consistent usage with express-session – npm. The data to be stored is hung on the session.

Under the default configuration, when I give him a userid, it will be saved like this:

The eyJ1c2VyaWQiOiJhIn0= here is just the base64 of {"userid":"abb"}.

“Tamper-proof”

Then the problem comes, if the user cdd gets
{"userid":"abb"} converted to base64, and then manually modified my token to
eyJ1c2VyaWQiOiJhIn0=, can I directly access abb’s data?

Yes. So depending on the situation, if the token involves sensitive permissions, you must find a way to prevent the token from being tampered with.

The solution is to add a signature to the token to identify whether the token has been tampered with. For example, in the cookie-session – npm library, add two configurations:

secret: 'iAmSecret',signed: true,

This will create a .sig cookie, the value inside is {"userid":"abb"} and iAmSecret calculated through encryption algorithms, common ones such as HMACSHA256 (System.Security.Cryptography) | Microsoft Docs.

Okay, now although cdd can forge eyJ1c2VyaWQiOiJhIn0=, it cannot forge the content of sig because he does not know the secret.

The difference between Token and Session

  • Session is a mechanism that records the session status of the server and client, making the server stateful and able to record session information. Token is a token, a resource credential required to access the resource interface (API). Tokenmakes the server stateless and does not store session information.
  • Session and Token are not contradictory. As an identity authentication Token, the security is better than Session, because each request is signed and can prevent monitoring and replay attacks, while Session must rely on the link layer to ensure communication security. If you need to implement a stateful session, you can still add Session to save some state on the server side.

Application solution: JWT

“How JWT works”

  • The user enters the username/password to log in. After the server authentication is successful, a JWT will be returned to the client.
  • The client saves the token locally (usually using localstorage, but can also use cookies)
  • When a user wants to access a protected route or resource, he or she needs to use the Bearer mode to add JWT in the Authorization field of the request header. Its content looks like the following
  • The server-side protection route will check the JWT information in the request header Authorization. If it is legal, the user’s behavior will be allowed.
  • Because JWT is self-contained (contains some session information internally), it reduces the need to query the database
  • Because JWT does not use cookies, you can use any domain name to provide your API services without worrying about cross-origin resource sharing (CORS)
  • Because the user’s status is no longer stored in the server’s memory, this is a stateless authentication mechanism.
Authorization: Bearer <token>
“JWTDetails

The first part we call it the header, the second part we call the payload (similar to the items carried on an airplane), and the third part is the signature.

header

The header of jwt carries two parts of information:

  • Declaration type, here is jwt
  • Declaring the encryption algorithm usually uses HMAC SHA256 directly

The complete header looks like the following JSON:

{
  'typ': 'JWT',
  'alg': 'HS256'
}

The header is then base64 encrypted (the encryption can be symmetrically decrypted), forming the first part.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
playload

The payload is where valid information is stored. This name seems to refer specifically to the cargo carried on the plane. This effective information contains three parts:

  • Statement registered in the standard
  • public statement
  • private statement

Statements registered in the standard (recommended but not mandatory):

  • iss: jwt issuer
  • sub: Users targeted by jwt
  • aud: The party receiving jwt
  • exp: The expiration time of jwt. This expiration time must be greater than the issuance time.
  • nbf: Defines the time before which the jwt is unavailable.
  • iat: jwt issuance time
  • jti: The unique identity of jwt, mainly used as a one-time token to avoid replay attacks.

Public Statement:
The public statement can add any information, generally adding user-related information or other necessary information required by the business. However, it is not recommended to add sensitive information because this part can be decrypted on the client.

Private declaration:
The private statement is a statement jointly defined by the provider and the consumer. It is generally not recommended to store sensitive information because base64 is symmetrically decrypted, which means that this part of the information can be classified as plain text information.

Define a payload:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

Then base64 encrypt it to get the second part of Jwt.

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature

The third part of jwt is a visa information. This visa information consists of three parts:

  • header (after base64)
  • payload (after base64)
  • secret

This part requires the base64-encrypted header and base64-encrypted payload to be connected using . to form a string, and then salted secret combined encryption through the encryption method declared in the header. , and then constitutes the third part of jwt.

// javascript
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

var signature = HMACSHA256(encodedString, 'secret'); // TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

Use . to connect these three parts into a complete string to form the final jwt:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7H gQ

Note: The secret is saved on the server side, and the jwt issuance is also generated on the server side. The secret is used for jwt issuance and jwt verification, so it is the private key of your server and should not be used in any scenario. Revealed. Once the client learns about this secret, it means that the client can self-sign jwt.

“Advantages”
  • Because of the versatility of json, JWT can be supported across languages, and many languages such as JAVA, JavaScript, NodeJS, PHP, etc. can be used.
  • Because of the payload part, JWT can store some non-sensitive information necessary for other business logic in itself.
  • Easy to transmit, the structure of jwt is very simple and the byte size is very small, so it is very easy to transmit.
  • It does not require saving session information on the server side, so it is easy to extend the application

The difference between Token and JWT

  • Token: When the server verifies the Token sent by the client, it also needs to query the database to obtain user information, and then verify whether the Token is valid.
  • JWT: The Token and Payload are encrypted and stored on the client. The server only needs to use key decryption for verification (verification is also implemented by JWT itself). There is no need to query or reduce the query database, because JWT self-contains the user information and encrypted data.