About JWT what you should know

Time:2021-11-20

What is JWT?

The full name of JWT is JSON web token, which is a very lightweight specification. This specification allows us to use JWT to transfer safe and reliable information between users and servers.

JWT usage scenarios

jwtExtensive use of drawings, e.gto grant authorizationauthentication Wait. To be more specific, if we have a user a who wants to invite a user to his group, then user a needs to generate an invitation link, and the content of the link is roughly as follows:https://host/group/{group_id}/invite/{invite_user}

At this time, clicking this link can enable users to join the group, but users can change the parameters of this link at will, such as changing the ID behind the group to join any other group, changing the invitee behind the invite, and so on. So this URL is not secure, so in this case, we can use itjwtTo create a secure invitation link.

First, the URL should be changed simply,https://host/group/invite/{token}
You can see that we removed the groupid and inviteuser parameters and added onetokenParameters. It is conceivable that groupid and inviteuser should be includedtokenInside, how to implement this seemingly magical token? Let’s see how JWT works.

Composition, principle and implementation of JWT

Before you talk about the principle of JWT, you have to know what JWT consists of.

JWT composition

A JWT is actually a string, which consists of three parts: header, payload and signature.

Header

JWT needs a header to describe the most basic information about the JWT, such as its type and the algorithm used for signature. This can also be represented as a JSON object, such as:

{
  "typ": "JWT",
  "alg": "md5"
}

After encoding the above JSON string with Base64, you can get the following content, which we call JWT header.

eyJ0eXAiOiJqd3QiLCJhbGciOiJtZDUifQ==

Load (payload)

Let’s first describe the above operation of inviting to the group as a JSON object. Some other information is added to help the server receiving the JWT understand the JWT in the future.

{
    "sub": "1",
    "iss": "http://host/group/invite",
    "iat": 1451888119,
    "exp": 1454516119,
    "nbf": 1451888119,
    "jti": "37c107e4609ddbcc9c096ea5ee76c667",
    "group_id": 1,
    "invite_user": "A"
}

The first six fields are all created by JWTstandardDefined.

  • Sub: the user the JWT is targeting
  • ISS: issuer of the JWT
  • IAT (issued at): when is the token issued
  • Exp (expires): when does the token expire
  • NBF (not before): token cannot be received and processed before this time
  • JTI: the JWT ID provides a unique identifier for the web token

After encoding the above JSON string with Base64, you can get the following content, which we call JWT payload.

eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvOiIsImV4cCI6MTUyNzY2NzY2MywiaWF0IjoxNTI3NjY0MDYzLCJuYmYiOjE1Mjc2NjQwNjMsImdyb3VwX2lkIjoxLCJpbnZpdGVfdXNlciI6IkEiLCJqdGkiOiJlMjE4ZTJhZDdlYTdmZjUzYTVhM2RlZjA0MmFjMjM4NCJ9

Signature

Before signing, we need to get the string for signing, and use the header and payload.Splice (head first) to get the string for signature

eyJ0eXAiOiJqd3QiLCJhbGciOiJtZDUifQ==.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvOiIsImV4cCI6MTUyNzY2NzY2MywiaWF0IjoxNTI3NjY0MDYzLCJuYmYiOjE1Mjc2NjQwNjMsImdyb3VwX2lkIjoxLCJpbnZpdGVfdXNlciI6IkEiLCJqdGkiOiJlMjE4ZTJhZDdlYTdmZjUzYTVhM2RlZjA0MmFjMjM4NCJ9

Then use the signature method to sign the string used for signature to obtain the following string, namely signature

NDljMzljOTkyOGNmYWU1NGEyZDYzMTk5NTNlNGEwZDA=

Finally, the string used for signature and signature are used.Splice (after signature) to get a completetoken。 However, at this time
tokenWithout the signer’s unique logo, it can be forged. As for how to solve this problem, we will talk about the following JWT specific implementation.

JWT principle

JWT how to ensure safety?

After finishing the composition of JWT, I believe you already know what JWT is – it’s a string!!!
So how does this string ensure that it is not tampered with? Here we need to introducesecretYes.

Go back to the above example and invite users to join the group. Although we changed the parameter to the form of token above, you may find that after such a token is captured by others, you can still forge a similar token by yourself because the signature at this time(Signature)There is no unique identity information of the issuer, and all data is in clear text, so such signature is unsafe and should be addedsecretSign.

The issuer needs to prepare a string that can confirm his identity. This string is calledsecret。 withmd5Take the signature method as an example(MD5 is not recommended as a signature method), we just need to simply match the string prepared above for signature withsecretSplice and then perform MD5 calculation. At this time, the obtained signature is protectedsecretValue, so even after others capture ittoken, he still can’t tamper with the contents of the token at will because he doesn’t knowsecretAnd splicing method, so it is necessary at this timetokenIt is safe and cannot be tampered with maliciously.

$signatureString = 'pen'; //  raw data
$secret = 'apple';        //  Issuer secret

$originSignature = md5($signatureString .'-'. $secret);
print_r($signature); // apple-pen 

$signatureString = 'pen'; //  raw data
$secret = 'pineapple';    //  Different secret 

$fakeSignature = md5($signatureString .'-'. $secret);
print_r($signature); // pineapple-pen 
//You can see that different secrets will generate completely different signatures, so our data can be guaranteed not to be tampered with at will~

Will the data transmitted by JWT be leaked?

Yes, both the header and payload fields of JWT can be decoded(Base64 belongs to encoding and can be decoded)。 Therefore, it is not recommended to use JWT to transmit sensitive information, such as passwords, because it is easy to be captured, decoded and stolen.

Secret a string is not enough to describe the issuer information?

We can describe the issuer information as a JSON, and then encode the JSON string, so we can also get a secret string.

JWT implementation

Let’s start with the roughest JWT implementation

The simplest and roughestjwt for phprealization

class JWT
{
    protected $headers;

    protected $payload;

    /**
     * @return array
     */
    public function getHeaders(): array
    {
        return $this->headers;
    }

    /**
     * @return array
     */
    public function getPayload(): array
    {
        return $this->payload;
    }

    public function __construct(array $headers, array $payload)
    {
        $this->setHeaders($headers);
        $this->setPayload($payload);
    }

    public function setHeaders(array $headers): void
    {
        $this->headers = $headers;
    }

    public function setPayload(array $payload): void
    {
        $this->payload = $payload;
    }

    /**
     *Gets the string used for signing
     *
     * @return string
     */
    public function signatureStr(): string
    {
        $headersStr = $this::encodeStr(json_encode($this->headers));
        $payloadStr = $this::encodeStr(json_encode($this->payload));

        return "{$headersStr}.{$payloadStr}";
    }

    /**
     *Code
     *
     * @param string $string
     *
     * @return string
     */
    protected static function encodeStr(string $string): string
    {
        return rtrim(strtr(base64_encode($string), '+/', '-_'), '=');
    }

    /**
     *Decode
     *
     * @param string $string
     *
     * @return string
     */
    protected static function decodeStr(string $string): string
    {
        return base64_decode(strtr($string, '-_', '+/'));
    }

    /**
     *Signature. At this time, the secret is qbhy
     *
     * @param string $string
     *
     * @return string
     */
    protected static function signature(string $string): string
    {
        return md5($string . 'qbhy');
    }

    /**
     *Verification signature
     *
     * @param string $signStr
     * @param string $sign
     *
     * @return bool
     */
    protected static function checkSignature(string $signStr, string $sign): bool
    {
        return static::signature($signStr) === $sign;
    }

    /**
     *Generate token
     *
     * @return string
     */
    public function token(): string
    {
        $signStr = $this->signatureStr();

        $token = $signStr . '.' . $this::signature($signStr);

        return $token;
    }

    /**
     *Get data from token
     *
     * @param string $token
     *
     * @return \App\Modules\JWT\JWT
     * @throws \App\Modules\JWT\JWTException
     */
    public static function fromToken(string $token): JWT
    {
        $arr = explode('.', $token);

        if (count($arr) !== 3) {
            Throw new jwtexception ('token error ');
        }

        if (!static::checkSignature("{$arr[0]}.{$arr[1]}", $arr[2])) {
            Throw new jwtexception ('signature error ');
        }

        $headers = json_decode(static::decodeStr($arr[0]), true);
        $payload = json_decode(static::decodeStr($arr[1]), true);

        return new static($headers, $payload);
    }

}

simple-jwt

Here is a JWT extension package based on PHP written by Amway—96qbhy/simple-jwt, this package implements the complete JWT specification out of the box. You can use it based on96qbhy/simple-jwtTo add JWT related functions to your app.

I split simple JWT into,EncoderEncrypter (signer)JWTJWTManagerFour parts, you can expand yourselfEncoderEncrypterIn order to realize their own coding and encryption methods, interested students can gogithubLook at the source code96qbhy/simple-jwt。 If you have any questions, you are welcome to discuss with me andIssueandPR
If you have any mistakes, please point them out. Thank you.