geektimes

Уязвимости и платежные сервисы

  • среда, 5 ноября 2014 г. в 02:10:37
http://habrahabr.ru/post/242245/

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

В этой статье я расскажу о некоторых распространенных ошибках, которые допускают программисты при разработке платежных сервисов, а также приведу примеры последствий, к которым они могут привести.

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

Заранее предупрежу, я специализируюсь на поиске уязвимостей, реализуемых через HTTP протокол и соответственно все примеры будут с использованием этого протокола.

Для простоты, там, где это возможно. я буду описывать пример в виде GET запроса вместо POST для более удобного восприятия.

И так, поехали…

Пример №1. Отсутствие проверки прав доступа при запросе с использованием идентификатора
Пример запроса:

http://my.site.com/getAccountInfo?id=12345

Это самая распространенная ошибка, которую я встречал в большинстве уязвимых сервисов. Как правило, в любом интерфейсе, часть запросов, которые может сформировать клиентская сторона, используют целочисленные идентификаторы и в большинстве случаев проверка прав доступа успешно осуществляется сервером. Но рано или поздно обнаруживается запрос, который не проверяется и в итоге нам удается получить личную информацию другого клиента. Такими данными на моем опыте были личные сообщения клиента саппорту, паспортные данные, банковские выписки. Также, бывали случаи, когда помимо доступа к персональным данным клиента, такая уязвимость позволяла осуществлять платежи со счета другого клиента. Обычно такие ошибки допускаются по нескольким причинам:
  • первая из них заключается в том, что в проекте нет системного архитектора и вопросы безопасности сервиса ложатся на плечи программистов
  • вторая причина — это когда серверное приложение взаимодействует с множеством источников хранения данных по различным протоколам и нет универсального решения для контроля за правами доступа к этим данным. В таком случае для каждого отдельного типа запроса разработчику нужно реализовывать конкретное решение по проверке прав, что довольно часто не делается.


Пример №2. Доступ к личным данным по GET запросу с использованием хэша без авторизации

http://my.site.com/getPaymentDetails?hash=c68f39913f901f3ddf44c707357a7d70

На моей памяти было несколько сервисов, которые допускали такую возможность. В одном случае это был банк, который возвращал по клиенту очень подробную информацию, а во втором случае сайт, предоставлявший услугу по переводам с карты на карту и по такому адресу возвращал сохраненную карту со сроком действия и cvv2, что, между прочим, строго запрещено правилами платежных систем Visa и MasterCard.
Ошибка программиста в том, что он думает, что ни кто не может узнать этот урл кроме самого пользователя. На самом деле любой сайт, на который вы перейдете с этой страницы по ссылке, получит ваш урл в поле Referer заголовка запроса. Также такой урл может проиндексировать поисковик и впоследствии выдавать его в поисковой выдаче.

Пример №3. Передача в ответе сервера избыточной, чувствительной информации, которая в итоге ни где не используется.
Допустим, у нас есть форма на странице, которая по ajax запросу должна отобразить какую-нибудь дополнительную информацию, например название мерчанта. Тогда запрос будет выглядеть примерно так:

http://my.site.com/getMerchantName?merch_id=12345

В ответе нам должно вернуться примерно следующее:

{merch_id:’12345’, name:’Test merchant’}

Но бывает так, что в ответе приходит не только название мерчанта, но и вся сериализованная модель объекта Merchant, который замаплен на таблицу в базе данных, и таким образом от сервера приходит и такая информация, как, например, пароль мерчанта

{merch_id:’12345’, name:’Test merchant’, password:’isdy5894yt8sdlg’, ...}

В совокупности с уязвимостью из
примера №1 эта ошибка дает возможность осуществлять операции от имени чужого мерчанта.
Так, в моей практике был случай, когда сайт интернет-банка по простому ajax запросу о лимитах возвращал чуть ли не всю имеющуюся информацию по клиенту, в том числе номера карт, их сроки действия, паспортные данные клиента, адреса прописки и проживания. Необходимости передавать всю эту информацию не было, так как пользователю отображалась только информация по лимитам, но программист видимо особо не вникая, всю информацию, полученную его приложением с внутреннего API банка, передавал на клиента. А так как эта ошибка была допущена им совместно с ошибкой из примера №2, личные данные клиента могли быть доступны третьим лицам.

Пример №4. Отсутствие CSRF токена — ключа, ассоциированного с сессией пользователя
Пример запроса:

http://my.site.com/createPayment?phone=0987654321&amount=100

Опасность такого запроса заключается в том, что если вы авторизованы на сайте, злоумышленник просто попросив вас перейти по такой ссылке, сможет в вашем аккаунте создать платеж на пополнение мобильного телефона.

Как правило, все эти ошибки встречаются не по одиночке, а в различных комбинациях друг с другом, что дает злоумышленнику ещё большее пространство для действий.

Естественно, большая часть этих уязвимостей не появилась бы при правильно построенной архитектуре приложения. Но как мы все знаем, не всегда на старте у проекта есть возможность нанять толкового, опытного архитектора, да и не всегда сам архитектор может предусмотреть все подводные камни, которые впоследствии возникнут в ходе дальнейшей реализации проекта.

Поэтому владельцам таких сервисов нужно уделять особое внимание тестированию на наличие уязвимостей и тут может активно помочь интернет-сообщество. Это отлично понимают такие интернет-гиганты как Google, Amazon, eBay, Yandex и для этого они организуют bug bounty программы, которые предусматривают поощрение за найденные уязвимости. Но я считаю, что такая программа посильна и для небольших проектов, которые только выходят на рынок. Это отличный способ улучшить защиту данных клиентов, избежать ненужной шумихи в СМИ из-за опубликованной кем-то уязвимости и сохранить имидж компании. Ну а желающие искать дыры в сервисах, я уверен, найдутся, тем более, что для этого будут законные основания и финансовая мотивация.