Message Encryption in JavaScript and PHP
Tags: php, javascript, node, cryptography
Published: 2019-10-20 21:34:26

---
title: Message Encryption in JavaScript and PHP
published: true
description: Encrypt in JavaScript, Decrypt in PHP; or vice-versa!
tags: php, javascript, node, cryptography
---

Just for fun, let's encrypt some stuff in client-side JavaScript and have a PHP server decrypt it. Note that **this [will never replace TLS (HTTPS)](https://stackoverflow.com/a/12327375/2224584)**.

### JavaScript Encryption with Sodium-Plus

You'll want the [latest release of sodium-plus](https://github.com/paragonie/sodium-plus/releases) for this. (As of this writing, it's [version 0.4.0](https://github.com/paragonie/sodium-plus/releases/tag/v0.4.0).)

```
<script
  src="/static/js/sodium-plus.min.js"
  integrity="sha384-lv7SVE0eb0bXA3fgK6PwlhViiUwG6tBuMAhS8XX7RvBvyRcdEdJ8HKtFgs4vHTUh"
></script>
```

Next, you'll want to write some JavaScript code to encrypt a message and send it to a server. I'll be using jQuery for this example, but you can easily adapt it to use a `XMLHttpRequest` object instead.

Let's define two functions. One loads a `CryptographyKey` object from a hard-coded string (n.b. you never want to actually do this, but for the sake of an easy, working example, we're using a hard-coded secret). The other actually encrypts a message.

```
/**
 * Get the example key. In the real world, you want to generate these randomly.
 */
async function getExampleKey() {
    if (!window.sodium) window.sodium = await SodiumPlus.auto();
    return CryptographyKey.from(
        'e9897cea109576c2f8088c277125d553e4f83afbc0abbb92cfb1f7b776b4fee0',
        'hex'
    );
    // return await sodium.crypto_secretbox_keygen();
}

/**
 * Encrypt a message under a given key.
 */
async function encryptMessage(message, key) {
    if (!window.sodium) window.sodium = await SodiumPlus.auto();
    
    let nonce = await sodium.randombytes_buf(24);
    let encrypted = await sodium.crypto_secretbox(message, nonce, key);
    return nonce.toString('hex') + encrypted.toString('hex');
}
```

Next, you'll want to write a function that gathers user input, encrypts it, and sends it to a server.

```
async function sendEncryptedMessage() {
    let key = await getExampleKey();
    let message = $("#user-input").val();
    let encrypted = await encryptMessage(message, key);
    $.post("/send-message", {"message": encrypted}, function (response) {
        console.log(response);
        $("#output").append("<li><pre>" + response.message + "</pre></li>");
    });
}
```

...and some supporting HTML:

```
<label for="user-input">Type a message to encrypt and send:</label>
<textarea id="user-input"></textarea>
<button id="send-it" type="button">Send Encrypted Message</button>
<hr />
<ol id="output"></ol>

<script type="text/javascript">
$("#send-it").on('click', sendEncryptedMessage);
</script>
```

### PHP Decryption with Sodium

You're going to want [paragonie/sodium_compat](https://github.com/paragonie/sodium_compat).

If you're using PHP 7.2, with overwhelming probability you can just use the built in `sodium_*` functions. However, some distros *may* incorrectly disable the sodium extension by default. So to play it safe, install sodium_compat anyway.

If you're using a framework (Symfony, Laravel), your code will look a lot cleaner, but for the sake of illustration, the decryption code will look like this:

```
<?php
declare(strict_types=1);

require 'vendor/autoload.php'; // Composer

header('Content-Type: application/json');

$key = sodium_hex2bin('e9897cea109576c2f8088c277125d553e4f83afbc0abbb92cfb1f7b776b4fee0');

$encrypted = $_POST['message'] ?? null;
if (!$encrypted) {
    echo json_encode(
        ['message' => null, 'error' => 'no message provided'],
        JSON_PRETTY_PRINT
    );
    exit(1);
}

$nonce = sodium_hex2bin(substr($encrypted, 0, 48));
$ciphertext = sodium_hex2bin(substr($encrypted, 48));
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);

echo json_encode(
    ['message' => $plaintext, 'original' => $encrypted],
    JSON_PRETTY_PRINT
);
```

### Putting it Together

When you type in a message and press the button, it will encrypt it and send a hex-encoded string to the server.

The PHP code will then decrypt the message and return the plaintext in a JSON response.

The JavaScript code will then grab the plaintext from the JSON response and append it to the output field below the form.

## Security Considerations

This is just a toy example to illustrate how to use [sodium-plus](https://github.com/paragonie/sodium-plus) (JavaScript) and libsodium (PHP) to encrypt/decrypt messages.

We took *a lot* of shortcuts that you won't want to take in a real system (for example: hard-coding the encryption keys, and eschewing error-checking in favor of brevity).

If you'd like to do something more advanced ([public-key encryption in JavaScript](https://github.com/paragonie/sodium-plus/blob/master/docs/SodiumPlus/sealed-boxes.md) and the [congruent PHP functions](https://paragonie.com/book/pecl-libsodium/read/08-advanced.md#crypto-box-seal)), the documentation is available for free online.

Shameless plug: If you're looking for security experts to review your JavaScript or PHP code, check out [why you may want to hire Paragon Initiative Enterprises for code audits](https://paragonie.com/blog/2017/06/why-you-want-paragon-initiative-enterprises-audit-your-code).

## Further Reading

* [The dangers of browser JavaScript cryptography](https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/august/javascript-cryptography-considered-harmful)
* [Improving the Cryptography of the JavaScript Ecosystem](https://paragonie.com/blog/2019/10/improving-cryptography-javascript-ecosystem)
* [Sodium-Plus: A Positive Cryptography Experience for JavaScript Developers](https://dev.to/paragonie/sodium-plus-a-positive-cryptography-experience-for-javascript-developers-2p08)