B4J Tutorial [BANanoVuetifyAD3] Encrypting & Decrypting Traffic between JavaScript (Encrypt) & PHP (Decrypt)

Ola

DISCLAIMER: ALWAYS RUN YOUR PHP WEBSERVER ON TOP ON SSL

Download Source code to REPRODUCE (Extracted from the BVAD3 Library)

Whilst you might be secure when running PHP apps on top of SSL, the issue at times is internal and the risks it poses. Anyone who has access to your app can access it and be able to see what happens in the backend if you do PHP CRUD or any ajax/fetch call. Case in point this example.

This example addresses how to encrypt and decrypt data between javascript & php. The example deals about dynamic SQL statements generated at client side and sent to the PHP Webserver. As my good friend puts it, I quote, "it is to stop novice users from peaking into the F12, and to discourage intermediate users from trying."

To be able to see what is being sent by BANano.CallInlinePhpWait, open developer tools, select network, select payload, execute some crud functions.

1649491623592.png


Whilst this is just an example of what one should not do client side (the client app must never know what happens in your backend), in most situations we are faced with building SQL queries client side and needing to be sent to the server for processing.

What if there was a way to change this by encrypting and decrypting the traffic between JavaScript & PHP? Fortunately there is. I will depict the plan to achieve this below.

js2php1.png



Step 1

A person would normally click a Save button, we want to get the form content and encrypt it before we can send to the PHP webserver for processing. Remember, even if we were sending only arguements to php they would be visible as demonstrated above. Below we just build a map, convert it to json and then encrypt it to demonstrate the first step.

B4X:
Dim b As Map = CreateMap()
    b.Put("command", "insert")
    b.Put("query", "INSERT INTO users (firstname, lastname) VALUES (?, ?)")
    Dim largs As List
    largs.Initialize
    largs.Add("Anele")
    largs.Add("Mbanga")
    b.Put("args", largs)
    Dim ltypes As List
    ltypes.Initialize
    ltypes.Add("s")
    ltypes.Add("s")
    b.Put("types", ltypes)
    Dim makeJSON As String = BANano.ToJson(b)
    Dim xxx As String = Encrypt4Php(makeJSON, "password")
 
Last edited:
  • Like
Reactions: LJG

Mashiane

Expert
Licensed User
Longtime User
If we log the encrypted record to send to the db, we get this...

1649493474070.png


This is sent to the server with BANano.CallinlinePHPWait..

B4X:
Log("Save data to database...")
    Dim Result As String = BANano.Await(BANano.CallInlinePHPWait("BANanoMySQLED", BuildED(xxx)))
    Log("Encypted Response received from php...")
    Log(Result)

This is better than the visible content demonstrated above and checking the network log payload, no one can even determine what that means.

1649493560749.png


This concludes the first leg of the race and the JavaScript portion of the sending to the server.

We need the PHP webserver to decrypt and save the data to the db.
 

Mashiane

Expert
Licensed User
Longtime User
The server receives it, decrypts it using PHP and then opens the db, processes the request being send and then sends an encrypted response back to the client.

1649493867138.png


After the content is decrypted in the db, it becomes the original content that was sent..

1649493957155.png


The server processes the SQL statement, encrypts the response and sends it with an echo back to the client...

1649494095190.png
 
  • Like
Reactions: LJG

Mashiane

Expert
Licensed User
Longtime User
When the client receives the encrypted content (the result of the CallInlinePhpWait), it processes this.

The content is decrypted, becomes a JSON, then converted back with BANano.FromJSON to a map.

B4X:
Dim yyy1 As String = Decrypt4Php(Result, "password")
    Log(yyy1)
    Dim m1 As Map = BANano.FromJson(yyy1)
    Log(m1)

1649494234201.png


We have been testing with the same records, so our phpMyAdmin is telling us the records are indeed added.

1649494281863.png


This closes the challenge, as we have been able to encrypt content at client side before sending it to the php webserver, we then executed the SQL statement, got the result and then sent it back to the client.

On receipt in client side, this is decrypted and any UI updates done.
 
  • Like
Reactions: LJG

Mashiane

Expert
Licensed User
Longtime User
What happens on the PHP side of things, lets take a peek at the encrypt code..

B4X:
public function encrypt(string $string, string $key): string
    {
        $ivLength = openssl_cipher_iv_length($this->encryptMethod);
        $iv = openssl_random_pseudo_bytes($ivLength);
 
        $salt = openssl_random_pseudo_bytes(256);
        $iterations = 999;
        $hashKey = hash_pbkdf2('sha512', $key, $salt, $iterations, ($this->encryptMethodLength() / 4));
        $encryptedString = openssl_encrypt($string, $this->encryptMethod, hex2bin($hashKey), OPENSSL_RAW_DATA, $iv);
        $encryptedString = base64_encode($encryptedString);
        unset($hashKey);
        $output = ['ciphertext' => $encryptedString, 'iv' => bin2hex($iv), 'salt' => bin2hex($salt), 'iterations' => $iterations];
        unset($encryptedString, $iterations, $iv, $ivLength, $salt);
        return base64_encode(json_encode($output));
    }// encrypt
 
  • Like
Reactions: LJG

Mashiane

Expert
Licensed User
Longtime User
The JavaScript side is rather interesting too...

B4X:
encrypt(string, key) {
        var iv = CryptoJS.lib.WordArray.random(16);// the reason to be 16, please read on `encryptMethod` property.
        var salt = CryptoJS.lib.WordArray.random(256);
        var iterations = 999;
        var encryptMethodLength = (this.encryptMethodLength/4);// example: AES number is 256 / 4 = 64
        var hashKey = CryptoJS.PBKDF2(key, salt, {'hasher': CryptoJS.algo.SHA512, 'keySize': (encryptMethodLength/8), 'iterations': iterations});
        var encrypted = CryptoJS.AES.encrypt(string, hashKey, {'mode': CryptoJS.mode.CBC, 'iv': iv});
        var encryptedString = CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
        var output = {
            'ciphertext': encryptedString,
            'iv': CryptoJS.enc.Hex.stringify(iv),
            'salt': CryptoJS.enc.Hex.stringify(salt),
            'iterations': iterations
        };
        return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(JSON.stringify(output)));
    }// encrypt

#Happy BVAD3 Coding.
 
  • Like
Reactions: LJG

Mashiane

Expert
Licensed User
Longtime User
To add more to this topic, when ones payload is visible, its easy to convert it to other formats and then change the data and execute it, as indicated by the Copy menu here. Thus the need rather to send encrypted data to the server which can then be decrypted that side.

1649521731477.png


As an example, clicking copy as cURL (cmd), gives out this output

B4X:
curl "https://localhost/bvad3ks/bvad3ks.php" ^
  -H "Accept: */*" ^
  -H "Accept-Language: en-US,en;q=0.9" ^
  -H "Connection: keep-alive" ^
  -H "Content-Type: text/plain;charset=UTF-8" ^
  -H "DNT: 1" ^
  -H "Origin: https://mashiane.github.io" ^
  -H "Referer: https://mashiane.github.io/" ^
  -H "Sec-Fetch-Dest: empty" ^
  -H "Sec-Fetch-Mode: cors" ^
  -H "Sec-Fetch-Site: cross-site" ^
  -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36" ^
  -H "sec-ch-ua: ^\^" Not A;Brand^\^";v=^\^"99^\^", ^\^"Chromium^\^";v=^\^"100^\^", ^\^"Google Chrome^\^";v=^\^"100^\^"" ^
  -H "sec-ch-ua-mobile: ?0" ^
  -H "sec-ch-ua-platform: ^\^"Windows^\^"" ^
  --data-raw "^{^\^"request^\^":^\^"BANanoMySQL^\^",^\^"params^\^":^{^\^"command^\^":^\^"select^\^",^\^"query^\^":^\^"SELECT * FROM `categories` WHERE `id` = ? ORDER BY `id`^\^",^\^"args^\^":^[^\^"27^\^"^],^\^"types^\^":^[^\^"s^\^"^]^}^}" ^
  --compressed


It would be nice though to achieve something more along these lines...


For now this will do.

#Happy BVAD3 Coding.

PS: Am making the option of sending the data back as encrypted / not, optional.
 

alwaysbusy

Expert
Licensed User
Longtime User
I honestly fail to see how this adds any level of security. Do you have an online demo available that demonstrates this concept so I may see it at work? I may be missing something here, and I would like to know what. A false sense of security is often worse.

From the description in the link in your last post I get the added level (Public/Private key handshaking), but not from your implementation
 

Mashiane

Expert
Licensed User
Longtime User
Do you have an online demo available that demonstrates this concept so I may see it at work?
As soon as I finish the planned implementation as per clients requests I will make an example available, just doing some final touches.

Remember... I said...

It would be nice though to achieve something more along these lines...
Referring to the provided link.
 

Mashiane

Expert
Licensed User
Longtime User
This functionality is now part of BVAD3 MySQL PHP (ONLY) implementation & can be set to be optional, with the DataSource. You can see this reel for more details of the Kitchen Sink Example. The download on post #1 is extracted from the library and is included for anyone who wants to REPRODUCE this behaviour to do so.

1. Note the DB_KEY in the mysqlconfig.php.
2. Note the AppStart BANano.Header.AddJavaScript
3. Note the 3 files for encryption in the Files Tab
4. The rest of the code is in pgIndex





PS: The recommended approach to implement a secure approach is indicated on post #8. This is just to encrypt & decrypt traffic between javascript & php.


#Happy BVAD3 Coding
 

alwaysbusy

Expert
Licensed User
Longtime User
The recommended approach to implement a secure approach is indicated on post #8
100%! Unless this WebApp runs on a secured intranet and is NOT accessible from the internet, any 12 year old with minimal scripting knowledge can decrypt the payload and start doing some evil stuff.

1. Just sniff out the encrypted payload e.g.
B4X:
eyJjaXBoZXJ0ZXh0IjoiWHNZSHpwS21JbzFBNFRnRHlrWUFXZmRoY29DWlRpd1R1cHV3RmR6VlowTE5MWXE2OW95SkdWamxBOUFWUHllWEM2MnhvZGwrLzR1TUZGejU0b2pCTjVyTWloU0xuTUVBOXVSakxkV3VoS29STklFZ0pDYksrUzlYVEI0WVJDSWlKWFZvQmFKTW1GbnJhUEdLeDNucDN1ZzNuSkVLS1lOTm8wb0hzL0lxa1BRRnlwNzZWVWRtdElPb1ZmdkdzNmkvIiwiaXYiOiI4NmNmYjQzMGM4ODc0NmJkZGVmNjA0NTNmN2IwNmY4YyIsInNhbHQiOiJhMzQ4NDgxZThlZGZkNzRkNDk5YTE4ZjBmM2Y1ZDM0OGVmMzg4ZGQxNDA5ZjUwNDE5NTM2MWNlYjNlZTkwZWQwYjU4OGRmMzE3ZWNkOWMxNmM0Zjc5NTE2ZTA3NDhjMjVjNTMwNjBlMWE0ZjJlMTc0ZDQxOTZhZDQ3MTkxM2FkNjNlODY3MGY3M2RlMjA3NjA0MjdlN2EwZGRiNTBmNzdkMGU1MjZmMjExOWFlZWFmNDYxMjNjMDczZTQ5NjE5ZDhhNGJjZjcxN2UxZmJkNGY2ZTUzM2VhMGM5NDY5ZGY2ZTljYTRiZmEyNGMwNTA5NDgzNmQzY2JkNmI4ZDBmYTY4OTY3N2Y4ODVmNWU5MTc1ZWJkN2RjNDJjNmVkNDVmODMyNWMyNTRlODIyNjc4MTBlMWNkYzA0YmJlNDI5MjAyZDQ2M2RhMWQwN2E0NWRkMWFhNzY3MTUyNWIwNDA3MzU0YzlhYzExZDNjMDYxOWI2NTFhN2MxNzkyNDVhZDA1MjMxZDlhZThiNGZlZThkNjViNzc1NzcyNGVhZjRhODVhZWJmMzRlNTQ4ZjI4YTU5ZmU0M2ViYTc3YjFhNzlmZDRlODY3ZDNkYTMwYjE0Zjg0N2RhZDJjYjRkMWMxNDczZGFlODcwNjYzM2U3OGYyNGQ5ZjIwYjVlMzg2MTEwZDE4NCIsIml0ZXJhdGlvbnMiOjk5OX0=

2. In Chrome DevTools read the the JavaScript source code (and this is the big flaw that needs the key-pair implementation!) to find the password:

1649836994938.png


3. Write any new simple WebApp you run on your 'personal' server (include the JavaScript files you can simply get from the server of the target). Add a snippet like this:
var enc = new Encryption();
console.log(enc.decrypt(payload, password));

4. Decrypt the message
B4X:
{"command":"insert","query":"INSERT INTO users (firstname, lastname) VALUES (?, ?)","args":["Anele","Mbanga"],"types":["s","s"],"resout":"y"}

5. now you are ready to do some evil things like "DROP TABLE users"

So 2. should never be possible.

This is an excellent article on how encryption could be archived: https://www.section.io/engineering-education/implementing-public-key-cryptography-in-javascript/

Alwaysbusy
 
Top