Handling card payments yourself is complicated and expensive (requiring PCI compliance), so for many organisations it's often more economical to use a third party payment processor, such as PayPal or Google Checkout.
Generally, the vendor website will implement its own shopping cart (bespoke or off-the-shelf), and when the user goes to checkout, they are redirected to the payment processor. They then make the card payment, and are redirected back to the original website. The redirection to the payment processor is a critical step, and one that we have found is often incorrectly implemented, which can result in serious security issues in the application.
When the user is redirected to the payment processor, information about the transaction needs to be passed to the processor, such as the merchant's ID, the products included, and most crucially the value of the transaction.
Let's take a look at some example code for performing a transaction with PayPal, taken from official PayPal developer documentation.
<form action="https://www.paypal.com/cgi-bin/webscr" method="post">
<input type="hidden" name="business" value="email@example.com">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="item_name" value="Hot Sauce-12oz Bottle">
<input type="hidden" name="amount" value="5.95">
<input type="hidden" name="currency_code" value="USD">
<input type="image" name="submit" "border="0" src="https://www.paypalobjects.com/en_US/i/btn/btn_buynow_LG.gif" alt="PayPal - The safer, easier way to pay online">
<img alt="" border="0" width="1" height="1" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" >
When the user clicks this checkout button, the form will be POSTed to PayPal, which will then read the business name, item name, currency and amount from the POST request. As this information is all in clear text in the HTML of the page and in the POST request itself, an attacker can simply modify the "amount" parameter, and thus modify the price that they're paying. This can be done either by editing the HTML source of the page, or by intercepting the requests using a local proxy (such as Burp Suite). In some cases it's even easier, as the parameters are included in the URL of a GET request, such as the example below.
Of course, PayPal isn't the only payment processor, and this kind of attack can be performed against many other providers. The following HTML code, taken from the Google Checkout developer documentation, is also vulnerable.
<form method="POST" action=https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/123456... accept-charset="utf-8">
<input type="hidden" name="item_name_1" value="Peanut Butter"/>
<input type="hidden" name="item_description_1" value="Chunky peanut butter."/>
<input type="hidden" name="item_quantity_1" value="1"/>
<input type="hidden" name="item_price_1" value="3.99"/>
<input type="hidden" name="item_currency_1" value="USD"/>
<input type="hidden" name="ship_method_name_1" value="UPS Ground"/>
<input type="hidden" name="ship_method_price_1" value="10.99"/>
<input type="hidden" name="ship_method_currency_1" value="USD"/>
<input type="hidden" name="tax_rate" value="0.0875"/>
<input type="hidden" name="tax_us_state" value="NY"/>
<input type="hidden" name="_charset_"/>
<input type="image" name="Google Checkout" alt="Fast checkout through Google" src="http://checkout.google.com/buttons/checkout.gif?merchant_id=1234567890&w..." height="46" width="180"/>
There are a number of different methods that payment processors use to prevent this vulnerability. The first is the use of a "secure hash" or signature, whereby a checksum or signature for the transaction information is calculated using a key known only by the website owner and the payment processor, and is included with the transaction. If this checksum does not match the data submitted, then this indicates that the transaction parameters have been tampered with, and the transaction is rejected. Another method is to pass the transaction details directly between the two servers, so that the client is only ever given a transaction ID. This means that the transaction parameters are never seen by the client, so they have no opportunity to tamper with them. Some payment processors also allow you to encrypt the entire transaction using public/private key cryptography, so all the client can see is an encrypted blob of data. Note that the latter option of using server to server communication may increase the scope of PCI DSS compliance.
However, if these methods are not correctly implemented by the vendor, it is still possible to tamper with the transaction. An example of this is where the transaction details are passed by the user's browser to a page on the vendor's website, which then generates the secure signed transaction to send to the payment processor. Since the price is still being POSTed by the user, they can still intercept and tamper with the price.
So, how do you protect yourself against this kind of attack? The first step is to review the developer documentation provided by your payment processor, and find out whether they support secure hashes, encrypted transaction details, or other similar security measures. Be careful when you look at their documentation, because as with PayPal and Google Checkout above, while some of their documentation will discuss these security features, there will often be pages that show you only the basic (or "getting started") instructions; the insecure methods. Review the code on your website to ensure that you're using these security features, and if you're not, get them implemented as soon as possible. If you're using an off-the-shelf shopping cart, get in touch with the developers and ensure that they've implemented these features as part of their product.
Bear in mind that just because you've implemented secure transaction processing, this doesn't necessarily mean that your processor will reject any insecure transactions. If an attacker can create their own insecure transaction request, they may still be able to exploit this vulnerability against your websites. Most payment processors will give you an option to reject insecure transactions, which you can set for your account..
Payment processors could help themselves, by deprecating and then removing the insecure APIs. Until that happens, ensure that you've evaluated the security features offered by your provider and that you've implemented them to protect yourself from fraudulent transactions.