Working With The Server Integration Method (SIM) Payment Gateway, Part 1: Don’t Use JavaScript

I got an email a few days ago from a reader seeking help with the Server Integration Method (SIM) credit card payment gateway.

Specifically, he was asking how he could use JavaScript to pass a calculated total to a PHP page that contains the SIM code.

  • A customer chooses a series of options from some select lists, radio buttons and the like;
  • the page calculates an order total;
  • the end user hits a submit button;
  • the results are posted to the SIM processing page, which acts as a “confirmation page”; and
  • The customer presses another button, which takes him to to provide payment info and actually charge the card.

You can see an approximation of what I’m talking about here: The questioner’s form is similar to this approximation in function.

I will show how to properly customize a SIM form, and submit payment requests to via SIM, in an upcoming post. This post explores why it’s a terrible idea to process order forms with JavaScript. That is, it’s about the wrong way to use SIM. Stay tuned for the right way.

The direct answer to my reader’s question is simple.

1. Add a hidden form field inside the form:

<input type="hidden" id="order_total_input" name="order_total_input" value="0.00" />

2. Using JavaScript, assign the calculated total as the value of that hidden form field:

function orderTotal() {
	var total;
	//bunch of code that calculates the total from various form inputs
	total = total.toFixed(2);
	document.getElementById('order_total_input').value = total;

3. Add this function as an onsubmit event for the form:

<form id="orderform" name="orderform" method="post" action="sim.php" onsubmit="orderTotal()">

You can see this in action here:

JavaScript Is Wildly Insecure

That said, this entire approach is wrong-headed, a complete security risk, and likely to backfire.

Never use JavaScript to calculate the values you intend to submit to a credit card processor. For what that’s worth, never use a hidden form fields to pass any variable to a credit card processor.

Always use server-side processing to check, sanitize and process credit card orders.

Why? Because anyone with a basic understanding of JavaScript can easily change the value of any form variable, simply by injecting a script onto your Web page.

You may have heard of Greasemonkey. It’s a Web browser plugin that allows any user to add JavaScript to any Web page. And many Web browsers allow you to add JavaScript to the current page simply by typing it into the location bar.

Using this knowledge, one can easily poison any form variable value on the page. He can not only poison the hidden form input that carries over the total to our shopping cart; he can also poison individual elements.

So, by the same token that the questioner can use this JavaScript to assign a value to order_total_input on form submission:

document.getElementById('order_total_input').value = total;

I can override the orderTotal() function to be just this one line:

document.getElementById('order_total_input').value = '0.01';

And that will set the total cost of my order to 1 cent.

You can see a demo of what I am talking about here:

Always Process Orders Server-Side

If you don’t check that value at some point, to ensure it is correct, you could easily wind up selling me your entire cart for a penny.

If you use JavaScript to confirm the order total, not only have you duplicated your efforts / code — which is the dictionary definition of inelegant — I can simply poison whatever JavaScript you used to confirm the order total is right, and still send along my one-penny charge.

Now, it’s true that many credit card processors will flag a one-cent charge as suspicious. Many will also flag a $1 charge. But most credit card processors consider a $1 hold entirely appropriate, as a request to confirm a card is valid, and indicative of a charge that will be posted later.

(If you buy gasoline at the pump using a credit card, you know this is how it’s usually handled: A $1 hold is placed on your card, and the actual total for gas is posted as an amended transaction a day or two later, depending on when the gas station’s merchant bank processes its queue.)

So a smart scammer wouldn’t use 1 cent to poison your variable. But if he saw a total charge, for several items, of $150, and one or two of the things he ordered cost $35, he would amend his total, via JavaScript, to $35.

That amount wouldn’t be flagged by the credit card processor, which has no idea what specifically is being bought; only how much you want to charge the customer. It sees a $35 charge, which it has probably processed numerous times on your behalf, as totally legitimate. Such as charge could easily slip past whomever is fulfilling your orders, especially if they are busy, and you haven’t engineered a program that properly checks order totals.

Any Form Variable Can Be Poisoned By DOM Manipulation

Admittedly, a significant part of the problem with the questioner’s approach is that he is using, as values in his form / JavaScript, the actual prices of what he is selling.

Since I know how much he wants to charge for each thing, and because he is calculating his order total based on values contained in HTML elements, I can simply change those values to be whatever I want them to be, via a little Document Object Model (DOM) manipulation.

So, if you have a select list that looks like this:

<select id="base_membership" name="base_membership" onchange="orderTotal()">
	<option value="0.00" selected="selected">No membership</option>
	<option value="50.00">Basic membership, $50 / year</option>
	<option value="80.00">Silver membership, $80 / year</option>
	<option value="100.00">Gold membership, $100 / year</option>					

There is nothing stopping me from writing a Greasemonkey script that changes the values for each of those options:

var tmp = document.getElementById('base_membership');
var i;
for(i = 0; i < tmp.length; i++) {
	tmp.options[i].value = parseFloat(tmp.options[i].value / 10).toFixed(2);

Now, his $50 membership only costs me $5, and the $100 membership is a bargain at $10.

You can see this in action here:

That is why, again, you should never use JavaScript to calculate a shopping cart total and always process credit card transaction totals on the server side.

I don’t mean to pile abuse onto my questioner.

Quite the opposite; I really appreciate the opportunity he’s provided to address the security issues around using JavaScript to accomplish important tasks, to discuss application security in general, and to set up the future discussion of how to properly implement’s Server Integration Method (SIM).

Again, I’ll do that soon.

All links in this post on delicious:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!