AES-256-CBC Encryption with CryptoJS and PHP OpenSSL

AES-256-CBC Encryption with CryptoJS and PHP OpenSSL
fastflux -- ink and watercolor, padlock, ethereal deco, detailed linework, soft color gradients, geometric elegance, ethereal landscapes, whimsical elements , gradient blending, enchanting, serene, magical realism

Data transfer encryption might trivial in general business perspective. But some businesses have critical and strong requirements to security. A minimal effort to implement data transfer security is to send our data via secure transport protocol such as HTTPS.

The strategy is to encrypt payload that sent to server via HTTP. The client-side should encrypt the payload, and the server-side should decrypt it. AES-256-CBC is one of many encryption methods out there. This article shares snippets on how to leverage AES-256-CBC encryption in client-side (CryptoJs) and decryption in server-side (PHP).

Which data I should encrypt?

The encryption can be done for many ways.

For each attribute's value requested to server, in example, you want to send username and password at login. You may encrypt each values so you'll send a payload like below.

POST /login
Content-Type: application/json

{
  username: "encryptedUsername",
  password: "encryptedPassword"
}

In above case, the encryption used twice at username and password.

You can also send the request's payload as full encrypted string. So the request you send will be like below.

POST /login
Content-Type: plain/text

_myencryptedadatapayloadhere_myencryptedadatapayloadhere_myencryptedadatapayloadhere_myencryptedadatapayloadhere_myencryptedadatapayloadhere_myencryptedadatapayloadhere

In above case, the encryption only happen once. The username and password can be transformed into many ways before encryption. Such as JSON, urlencoded style (username=uname&password=pwd), or using BASIC Auth style (uname:pwd).

Client Encryption

We can implement AES-256-CBC encryption with CryptoJs like below.

const aesEncrypt = (data, encryptionKey) => {
    const key = encryptionKey
    const vector = 'appkeyorelseasinitializationvector'

    const cipher = CryptoJS.AES.encrypt(
      data, 
      CryptoJS.enc.Utf8.parse(key), 
      {
        iv: CryptoJS.enc.Utf8.parse(iv),
        padding: CryptoJS.pad.ZeroPadding,
        mode: CryptoJS.mode.CBC
      }
    )

    return cipher.toString()
}

The aesEncrypt function will return an encrypted string of the given data. The data can be an object, array, or string.

Server Decryption

In server-side, we can implement the decryption method using openssl_decrypt like below.

protected function decrypt($data, $encryptionKey): false|string
{
    return openssl_decrypt(
        $data,
        'AES-256-CBC',
        $encryptionKey,
        OPENSSL_ZERO_PADDING,
        'appkeyorelseasinitializationvector'
    );
}

Just need to ensure that encryptionKey and vector must be the same value as the client-side.

Implementation

The implementation may vary like I mentioned previously.

If you're using Laravel and Inertia, you can use Axios interceptors to encrypt the payload before send, and then use Laravel Middleware to decrypt the payload. The decrypted payload can be put into Request object before it go into the controllers.