Skip to content

Instantly share code, notes, and snippets.

@robertogallea
Created May 8, 2020 08:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robertogallea/13ad697237fe73bf60102c1120d4bf71 to your computer and use it in GitHub Desktop.
Save robertogallea/13ad697237fe73bf60102c1120d4bf71 to your computer and use it in GitHub Desktop.

What's behind Laravel Encryption/Decryption

My system is safe, it uses encryption.

You heard/said this from time to time. Sure, but why and how is it safe? Do you really know this?

Laravel encryption/decryption fundamentals

Laravel encryption/decryption is based on the Illuminate\Encryption\Encrypter class, which is constructed passing an encryption key and a cipher (i.e. the encryption algorithm):

  • __construct($key, $cipher = 'AES-128-CBC')

It supports (among the others) the following main methods:

  • encrypt($value, $serialize = true)
  • decrypt($payload, $unserialize = true)

which, not surprisingly, are used to encrypt and decrypt data.

https://gist.github.com/4f1a73f316923576d6b0f2e96002c4fd

This is great! And it is enough to use it in the best way.

However, if you want to know what happens under the hood, keep reading.

Note that the results wouldn't be the very same since some values are computed randomly, so changes at every run.

How encrpytion works

Laravel's encrypter currently uses OpenSSL for performing AES-256 and AES-128 encryption. It also uses Message Authentication Code (MAC) protection, a mechanism to ensure data is not tampered after encryption.

What's in the result?

Recalling the previous example, you may think that the encrypted string "eyJpdiI6ImdMd2dWcW5jMXBrUDBranRJZXQ5MEE9PSIsInZhbHVlIjoiNnhTODBSclB3ZVp3SFRRUWFWTHpReFQwYWQ1aXVmTmhXOXV5WHM2TzR1WT0iLCJtYWMiOiIwODQyZDhiMzZlNDQwZTZjYTRiYmI2MGE0MTgzNzk5NGNkZTU1Yzc5NDIyYzdjYmYwNzk2ZTA5MGNjYjc4MGYzIn0=" is by itself the ciphered version of the input. This is definitely true, but there is more to know about.

Indeed, it is a base64 conversion of a string. "What string?" you may ask... And you can get an answer by simply run:

https://gist.github.com/0bb51b39654aad27dfcc5f3ab20d22c5

which results in a json string similar to the following:

https://gist.github.com/d946d651636aa676ecae01b51eb45930

Now is more opaque than clearer... What is this?

This document is composed by the three main parts of encryption:

  • value: the actual ciphered data, coded in base64
  • iv: the Initialization Vector is a randomly generated fixed-size data sequence inject at each run, preventing semantic-based attacks, see (Initialization vector - Wikipedia for more details). It is base64-coded too
  • mac: the Message Authentication Code is a signature used to detect value tampering, generated hashing value and iv. It is represented in hex-string format

Note that both iv and value are base64 encoded too, since they are generic bytes sequence and may contain not printable values.

How encryption works - looking at the code

To understand how the payload is generated, let's give a closer look to the encrypt() method:

https://gist.github.com/ffe9035a9a05efc294dca2ccfbeb1d71

Looking at the code, 5 steps are performed:

  1. Initialization Vector is generated on line 3 by generating 128 or 256 bits (according to the used cipher) of random data
  2. Encrypted Value is generated on lines 5-8 by running OpenSSL over a (possibly) serialized version of the clear text data, using the chosen cipher, encryption key and IV. Note that the results is base64-coded
  3. The MAC is generated by the hash() method, fed with base64 iv and value. Hashing is defined as: https://gist.github.com/910cafa37c8a20a1310e333a61d7e8d3

Looking at the code, 5 steps are performed:

  1. The json payload is extracted in line 3. During extraction it is also validated by ensuring that: 1.1. It has an array form 1.2. It contains iv, value and mac fields. 1.3. iv lenghts is compatible with cipher requirements 1.4. The mac is valid
  2. Data is decrypted using OpenSSL (lines 5-12)
  3. Result is (possibly) unserialized and returned

Why is it secure?

This scheme provides security until the encryption key is kept secret. Let's see why:

  • confidence: the clear text message can be recovered only by who knows the secret key
  • integrity: if value is modified, decryption fails. If iv and value are both modified, the message could be potentially decryptable, but MAC protection will detect tampering and decryption fails. In any case, varying any combination of iv and/or value and/or mac, decryption fails due to payload corruption.
  • The only way to deceive MAC protection is by knowing the encryption key, which allows to forge new valid ciphered full payloads.

Let's try: create a different encrypted message:

https://gist.github.com/01a300cfeb73cbdb1d8d32f71d159c2c

https://gist.github.com/d57c9af2720898224439062fdc088341

https://gist.github.com/858e59e1968df6772f3d104b3a1cb214

In each of the three cases the MAC control will fail preventing message decryption and DecryptException is raised.

Conclusion

Now you should understand more in detail how Laravel encryption works under the hoods. Nothing changes in how you use it, but you earned more confidence in the tools you used. Moreover, you can now justify with your customer "how" your system is safe.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment