Внимание: данную статью не следует воспринимать как руководство юного хакера; материал приведён исключительно в ознакомительных целях, чтобы программисты не повторяли подобных ошибок.
Итак, имеем платёжную форму (информация, идентифицирующая сайт, затёрта):

Ничего сверхестественного: нам предлагают купить некую услугу за $99 в месяц. Всё как обычно. Интересные вещи начинаются, когда смотришь на детали формы:

Для тех, кто не знает: сумма платежа для системы SecurePay задается как целое число (исходноё значение умножается на 100). Так, $99.00 передается как 9900, а, скажем, $20.45 — как 2045.
У любого человека, занимавшегося интеграцией платёжных систем и имеющего опыт в компьютерной безопасности, возникает законный вопрос: если скрипт перед отправкой платёжных данных обрабатывает эти самые данные, зачем помещать в форму итоговое значение, причем отформатированное для платёжной системы? Если этот человек имел дело с индопакистанофилиппинцами, то ему только остаётся схватиться за голову.
А что произойдет, если подменить значение на, скажем, 0100 ($1)? На нулевое менять нельзя, любая уважающая себя платёжка ругнётся; например, так:

Кстати, отрицательный результат — тоже результат: ошибку возвращает SecurePay, а из этого следует, что скрипт не проверяет значение суммы платежа, и ему можно скормить любое значение!
Итак, меняем сумму платежа:

И отправляем форму. В результате получаем такую картину:

Осталось посмотреть, сколько денег снялось:

Как видим, снялся $1.00 — операция прошла успешно.
Программистам (справедливости ради стоит отметить, что это были не индийцы, а румынец из компании SurgeWorks) могу сказать следующее: никогда не доверяйте данным, которые пришли от пользователя. Лень (и непрофессионализм) в этом случае могут обернуться большими потерями.
Update: чтобы ни у кого не возникало желание обвинить меня в обмане владельца сайта и взломе всяких разных сайтов, к которым я не имею никакого отношения (Jurgen, привет!): я поставил владельца сайта в известность, и мы с ним работаем над устранением уязвимости.

Меня зовут Владимир, я программист-фрилансер, специализирующийся на Web-программировании и програмировании под Linux.
По совместительству занимаюсь администрированием LAMP/LNMP-серверов и техническим переводом.





Сомнительно как-то все это… Мне кажется в современных, популярных платежных системах такие трюки не пройдут…
В частности, вот кусок кода с сайта:
//...
$a = array(
'merchantID' => $merchantID,
'password' => $password,
'amount' => $params['a'], //Вот оно пришло с формы - безо всяких проверок
'clientID' => $clientID,
'currency' => $currency,
'cardNumber' => $params['cc'],
'cvv' => $params['cvv'],
'expiryDate' => "{$params['expiry_date']['month']}/{$params['expiry_date']['year']}"
);
$xml = xmlForAddingTriggeredCreditCardPayment($a);
// Add customer to Securepay!
$response = securepayXMLtoArray( securepayProcess($payment_url, $xml, $debug) );
Программёр сэкономил три строчки:
$amount = $membership->trial_days > 0 ? $membership->trial_price : $membership->fee;
$amount *= 100;
А заказчик “нагрелся” на $98 с первой же попытки.
Чтобы не было путаницы с терминологией: SecurePay — это платёжная система, а скрипт, это та хрень, в которую интегрирована работа с SecurePay.