laravel的cookie并不简单

背景:

在之前的文章中,我已经阐述了laravel的cookie产生的过程,那laravel的cookie在响应到客户端之前,又经历了什么呢?

laravel的cookie生成以后,在响应到客户端之前,会经过encrypt的过程,encrypt的加密逻辑定义在src/Illuminate/Cookie/Middleware/EncryptCookies.php

    protected function encrypt(Response $response)
    {
        foreach ($response->headers->getCookies() as $cookie) {
            if ($this->isDisabled($cookie->getName())) {
                continue;
            }

            $response->headers->setCookie($this->duplicate(
                $cookie,
                $this->encrypter->encrypt(
                    CookieValuePrefix::create($cookie->getName(), $this->encrypter->getKey()).$cookie->getValue(),
                    static::serialized($cookie->getName())
                )
            ));
        }

        return $response;
    }

而上面的encrypt,会调用src/Illuminate/Encryption/Encrypter.php的encrypt方法

 

    public function encrypt($value, $serialize = true)
    {
        $iv = random_bytes(openssl_cipher_iv_length(strtolower($this->cipher)));

        $value = self::$supportedCiphers[strtolower($this->cipher)]['aead']
            ? \openssl_encrypt(
                $serialize ? serialize($value) : $value,
                strtolower($this->cipher), $this->key, 0, $iv, $tag
            )
            : \openssl_encrypt(
                $serialize ? serialize($value) : $value,
                strtolower($this->cipher), $this->key, 0, $iv
            );

        if ($value === false) {
            throw new EncryptException('Could not encrypt the data.');
        }

        $iv = base64_encode($iv);
        $tag = base64_encode($tag);

        $mac = self::$supportedCiphers[strtolower($this->cipher)]['aead']
            ? '' // For AEAD-algoritms, the tag / MAC is returned by openssl_encrypt...
            : $this->hash($iv, $value);

        $json = json_encode(compact('iv', 'value', 'mac', 'tag'), JSON_UNESCAPED_SLASHES);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new EncryptException('Could not encrypt the data.');
        }

        return base64_encode($json);
    }

 

看不懂?没关系,下面我将会逐个分析:

 

一、计算初始向量$iv

$iv = random_bytes(openssl_cipher_iv_length(strtolower($this->cipher)));

$this->ciphier,默认是:aes-128-cbc,在对cookie进行加密的时候使用的是:aes-256-cbc,据此可知:初始向量$iv,是一个由随机二进制的字节组成的长度为16的字符串

 

二、使用openssl_encrypt进行初步加密

    private static $supportedCiphers = [
        'aes-128-cbc' => ['size' => 16, 'aead' => false],
        'aes-256-cbc' => ['size' => 32, 'aead' => false],
        'aes-128-gcm' => ['size' => 16, 'aead' => true],
        'aes-256-gcm' => ['size' => 32, 'aead' => true],
    ];

因为对cookie进行加密的时候,使用的是:aes-256-cbc,对应的aead为false,因此初步加密,执行的是下面的代码

\openssl_encrypt(
                $serialize ? serialize($value) : $value,
                strtolower($this->cipher), $this->key, 0, $iv
            )

 

如果初步加密,返回false,抛出异常

        if ($value === false) {
            throw new EncryptException('Could not encrypt the data.');
        }

 

二、计算$mac

如果初步加密顺利,没有返回false,则计算$mac,以便于再次加密

        $iv = base64_encode($iv);
        $tag = base64_encode($tag);

        $mac = self::$supportedCiphers[strtolower($this->cipher)]['aead']
            ? '' // For AEAD-algoritms, the tag / MAC is returned by openssl_encrypt...
            : $this->hash($iv, $value);

 

计算$mac的过程,其实是对初始向量$iv和初步加密的$value,通过hash_hmac方法,使用sha256加密算法和私钥$this->key,进行hash的过程

    protected function hash($iv, $value)
    {
        return hash_hmac('sha256', $iv.$value, $this->key);
    }

 

三、将$iv、$value、$mac、$tag进行compact,然后再以JSON_UNESCAPED_SLASHES保留/的形式进行json_encode

$json = json_encode(compact('iv', 'value', 'mac', 'tag'), JSON_UNESCAPED_SLASHES);

 

如果json_encode出现异常,则抛出异常

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new EncryptException('Could not encrypt the data.');
        }

 

四、如果json_encode一切顺利,就以base64_encode方式对$json进行编码,响应最终的cookie

return base64_encode($json);

 

这就是laravel的cookie生成以后,在响应到客户端之前的完整加密过程。

 

posted @ 2022-04-30 15:22  jamstack  阅读(440)  评论(0编辑  收藏  举报