One big problem with JavaScript is that it is very hard for a developer to hide JavaScript code and to create secure data transfer between browser and server. It is always possible for someone to check XHR transfers and this makes data transfer very unsecure.
I had to deal this problem, because I had to develop sweepstakes application , which gave prizes to the user live. To make this happen I had to make secure session exchange between browser and server to synchronize FrontEnd and BackEnd.
I had the following options:
1. To create JSON transfer and use “cryptic” variable names
2. Create my own encryption algorithm
3. Use some kind of JavaScript AES/DES library
I decided to take the second option. My data was in text format and it was looking like this:
1 |
var1|1,var2|2,var3|3 |
Next step was to find a way how to encode this data in BackEnd and decode in FrontEnd. To do this I decided to use JavaScript bitwise XOR operator.
According to the documentation bitwise XOR operation works like this:
The ^ operator looks at the binary representation of the values of two expressions and does a bitwise exclusive OR operation on them. The result of this operation behaves as follows:
When one, and only one, of the expressions has a 1 in a digit, the result has a 1 in that digit. Otherwise, the result has a 0 in that digit.
In other words bitwise encoded string is created in two steps:
1. Partition the message into n-bit blocks
2. Bitwise XOR on the blocks
So the function had to split the string into blocks and make bitwise operation to the block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var jsEncode = { encode: function (s, k) { var enc = ""; var str = ""; // make sure that input is string str = s.toString(); for (var i = 0; i < s.length; i++) { // create block var a = s.charCodeAt(i); // bitwise XOR var b = a ^ k; enc = enc + String.fromCharCode(b); } return enc; } }; |
To decode the string, you can use exactly the same function again.
1 2 3 4 5 6 |
var e = jsEncode.encode("Hello world!","123"); // result 3[Z console.log(e); var d = jsEncode.encode(e,"123"); // result Hello world! console.log(d); |
*Next I had to base64encode/base64decode the string, because encoded string is in binary format and it is not possible to read it when doing XHR request. Luckily there are a lot of base64encode/base64decode solutions available for JavaScript on the internet.
It was time to create a PHP solution for BackEnd encoding. This task was much harder than I thought, because PHP ord function does not support multi byte characters.
I came up with the following class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
class jsEncode { /** * Encodes or decodes string according to key * * @access public * @param mixed $str * @param mixed $decodeKey * @return string */ public function encodeString($str,$decodeKey) { $result = ""; for($i = 0;$i < strlen($str);$i++) { $a = $this->_getCharcode($str,$i); $b = $a ^ $decodeKey; $result .= $this->_fromCharCode($b); } return $result; } /** * PHP replacement for JavaScript charCodeAt. * * @access private * @param mixed $str * @param mixed $i * @return string */ private function _getCharcode($str,$i) { return $this->_uniord(substr($str, $i, 1)); } /** * Gets character from code. * * @access private * @return string */ private function _fromCharCode(){ $output = ''; $chars = func_get_args(); foreach($chars as $char){ $output .= chr((int) $char); } return $output; } /** * Multi byte ord function. * * @access private * @param mixed $c * @return mixed */ private function _uniord($c) { $h = ord($c{0}); if ($h <= 0x7F) { return $h; } else if ($h < 0xC2) { return false; } else if ($h <= 0xDF) { return ($h & 0x1F) << 6 | (ord($c{1}) & 0x3F); } else if ($h <= 0xEF) { return ($h & 0x0F) << 12 | (ord($c{1}) & 0x3F) << 6 | (ord($c{2}) & 0x3F); } else if ($h <= 0xF4) { return ($h & 0x0F) << 18 | (ord($c{1}) & 0x3F) << 12 | (ord($c{2}) & 0x3F) << 6 | (ord($c{3}) & 0x3F); } else { return false; } } } |
Using class:
1 2 3 4 5 6 7 8 |
<?php $d = new jsEncode(); $enc = $d->encodeString('Hello world!','123'); // result 3[Z echo $enc; // result Hello world! echo $d->encodeString($enc,'123'); |
Now it was possible for me to encode data on the server side and decrypt data in the browser (or vice versa). To make my application even more secure, I also used JavaScript minifier / obfuscator. My favorite here is Google Closure Compiler. Even though it is very easy to deobfuscate your code when using services like jsbeautify , it is still much harder to read and understand.
Another and more secure way to encrypt / decrypt data is to use excellent cryptojs , but I found it too heavy for a small sweepstake application. If you have a bigger project, which needs more security, you should definitely check out cryptojs.
JsEncode source code is also available on my GitHub.