Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to guarantee correct Ajax calls parameter values

I am working on a website that requires some ajax calls in order to improve flexibility and performance. My ajax calls is for a ranking system. I need three input values to be processed with ajax (storeID, clientID, orderID). To submit an action with ajax, I want to make sure that the parameters values sent were not modified by users using web tools. So I was thinking of three different ways to guarantee that the information sent were not changed:

  1. Send an extra value that is the encryption of all the data sent all together. So at the server side while processing ajax, I can re encrypt the data sent and see if the encryption result matches the encryption value sent.

  2. Send all the data as one encrypted value. Then on the server while doing the ajax, I can decrypt the data and assign the values again.

  3. Send only the orderID and its encryption then using method (1) verify that the orderID is not changed, and using the database query, fetch the two other information.

Here's my opinion on each of the three ways:

  1. Consumes memory since I have to send orderID, clientID, storeID, encryptedID. Moreover, the information monitored in the ajax call will give people information about what happens when they rate an order.

  2. I checked online for mcrypt_encrypt and mcrypt_decrypt but I've never used them. I saw that they produce a long string, but I prefer to keep my data sent short or look like the md5 encrypted data. Is there better methods ?

  3. This is the elegant way, it looks straight forward but it needs some MySQL intervention, which could be time consuming especially when data grows in the future.

So which one do you think is better ? If you have more ways I appreciate if you share them here. Thank you

Example of scenario I want to avoid: Clicking a button will submit a form using AJAX by passing the Product ID. A user go to the source code, and change the ID of the product from X to Y. Now the user clicks the button and the form was submitted and the product of ID Y was be affected. As you can see, the parameter values sent are not secured and can be modified. I am searching for a way to guarantee that the parameter values sent are correct and not modified.

PS: This question is not about CSRF handling.

like image 747
CMPS Avatar asked Dec 02 '22 16:12

CMPS


2 Answers

Everyone is trying to lead you to these points:

On the server....

  1. You know who the user is (auth).
  2. You should define what that user can access (ACL).
  3. You should validate that the requested product both exists and is allowed to be accessed by the user.
  4. If the user is allowed to make changes to the product, then it is, by definition, within their rights to make a change to X product, and if they do it with Y product information because they are messing around in the source, then that is on the user.
  5. If this is shopping cart-related, then you should be ignoring all other data passed by the user (like, say, price) and load that product data by ID only. So they cannot buy Y product at X product's price. And then, if they enter in Y product's ID in Firebug, who cares?

And if you aren't doing 2 and 3, you're wasting your time completely on this idea. Whatever you come up with will only be adding obscurity to your insecurity.

like image 61
joefresco Avatar answered Dec 10 '22 02:12

joefresco


If you want to use encryption, you may use the Mcrypt functions with the Rijndael cipher. That gives you a block of 128 bits to play with.

Suppose your three IDs (storeID, clientID, orderID) are 32 bit each, you can pack those together with a 4 character string to form a 128 bit block. The string will be used to check the decrypted data later on.

$block = pack('a4LLL', 'MyID', $storeID, $clientID, $orderID);

Then create a random IV and encrypt the block:

$mod = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = openssl_random_pseudo_bytes(mcrypt_enc_get_iv_size($mod));
$mySecretKey = 'LA72U/aLEOcDoN0rqtM5sehNNjd7TUSILiBgI8bej6o=';

mcrypt_generic_init($mod, base64_decode($mySecretKey), $iv);

$encrypted = mcrypt_generic($mod, $block);

mcrypt_generic_deinit($mod);
mcrypt_module_close($mod);

You'll need to encode the encrypted block and the IV before using them client side. Using base64 and swapping the / and + characters gives you a nice value that can be sent as a GET parameter:

$encodedBlock = substr(strtr(base64_encode($encrypted), '/+', '-_'), 0, -2);
$encodedIV = substr(strtr(base64_encode($iv), '/+', '-_'), 0, -2);

Later when those values are sent to your ajax scripts, just reverse the process:

$encrypted = base64_decode(strtr($_GET['block'], '-_', '/+') . '==');
$iv = base64_decode(strtr($_GET['iv'], '-_', '/+') . '==');
$mod = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');

mcrypt_generic_init($mod, base64_decode($mySecretKey), $iv);

$block = mdecrypt_generic($mod, $encrypted);

mcrypt_generic_deinit($mod);
mcrypt_module_close($mod);

$data = unpack('a4str/Lstore/Lclient/Lorder', $block);

if ($data['str'] != 'MyID')
  throw new Exception('Something fishy is going on');

If all goes well $data will contain $data['store'], $data['client'] and $data['order'].

You may also pack a nonce value into the block in order to protect against replay attacks.

like image 42
Thomas Sahlin Avatar answered Dec 10 '22 03:12

Thomas Sahlin