Сегодня расскажу о том, как защитить in-app purchases в играх на мобильной платформе iOS с помощью собственного сервера. Практически все компании, которые выпускают свой продукт, заботятся о защищенности своих приложений и как можно больше пытаются защитить их от взлома. Одной из таких компаний является и та, в которой я работаю.

На данный момент на территории Испании проходит софтланч нашей игры. Сам я server-side developer и на мое плечо программиста легла проверка in-app purchases в игре, которую наша компания разрабатывала.

Все покупки в игре подтверждаются моим сервером. Происходит это следующим образом. Пользователь покупает в игре нашу валюту, тогда на смартфон от apple purchase server приходит json со всеми данными о покупке. После этого этот json попадает уже на наш сервер, сверяются некоторые поля и отправляются на apple verefication server, чтобы посмотреть, все ли хорошо. Если все в порядке, то от apple приходит json в котором есть много информации о покупке. Как утверждает документация на сайте Apple, нам достаточно проверить только поле status с присланного нам json. Если оно равно 0, то покупка правдивая и мы начисляем пользователю нашу валюту. Я ничего нового не придумывал и последовал этой документации.

В течение софтланча статистика нам показала, что один из пользователей купил нашей валюты в игре на 400 долларов. Однако мы не очень этому обрадовались, ведь видели, что эти покупки были с jailbreak смартфона. Посмотрев все данные, которые присылал девайс юзера нам на сервер, мы обнаружили, что они одинаковы. Немного поискав в интернете, мы натолкнулись на такую ??штуку как LocallAPStore Cydia Tweak в jailbreak iOS. Работает она следующим образом. Когда пользователь делает in-app purchase в игре, то LocallAPStore перехватывает данные и заменяет на свои, и возвращает callback игре, что покупка выполнена, ну а дальше оно приходит нам на сервер, отправляется в Apple. Нам приходит от apple verefication server response status 0.

Поэтому чтобы не допустить этого прежде всего проверяйте поле original_transaction_id, оно является уникальным, если вы в своей базе данных найдете идентичное значение этого поля — то это фрод. Но этого иногда бывает недостаточно. Для полной проверки уже после того как вам придет response json от apple verefication server следует сверить поле bid с уже вашим bundle id, а также сверять поле product_id.

Надеюсь вам будет полезной эта информация. Жду от вас комментариев, пожеланий и замечаний.

Комментарии (4)


  1. sadsanta
    03.11.2015 14:30
    +1

    Как утверждает документация на сайте Apple, нам достаточно проверить только поле status с присланного нам json.


    Ничего подобного документация не утверждает. Задача сервера сводится к проверке, что рецепт был подписан самим эпплом, и возврату этого рецепта в json.
    Вы никакой информации на сервер о принадлежности этого рецепта не передаёте. Откуда ему знать ваш это был рецепт или нет?

    Естественно, проверка соответствия bundle_id лежит на вашей совести, как и всех остальных параметров.

    если вы в своей базе данных найдете идентичное значение этого поля — то это фрод

    Или просто у вас очень преданный пользователь и он совершил вторую покупку. Предыдущая покупка из рецепта никуда не исчезнет.


    1. sadsanta
      03.11.2015 14:36

      Под рецептом я имею ввиду receipt, конечно, который на самом деле никакой не рецепт, а вовсе даже квитанция, просто я привык его так называть :)


    1. VGaldemar
      03.11.2015 17:22

      Ничего подобного документация не утверждает. Задача сервера сводится к проверке, что рецепт был подписан самим эпплом, и возврату этого рецепта в json.
      Вы никакой информации на сервер о принадлежности этого рецепта не передаёте. Откуда ему знать ваш это был рецепт или нет?


      Я согласен с вашим утверждением. Apple проверяет подписан ли ими receipt. Однако большинство разработчиков не учитывают тот факт, что нужно проверять другие поля и проверяют исключительно лишь поле status. Я написал это чтобы подчеркнуть другим разработчикам проверять и остальные поля, так как сам ранее не учитывал этот момент и следствием этого стало то, что в нашей игре могли купить нашу валюту с помощью ложных транзакций.

      Или просто у вас очень преданный пользователь и он совершил вторую покупку. Предыдущая покупка из рецепта никуда не исчезнет.


      Мы тестировали на реальных in-app purchase в игре с одного и того же устройства, и поле original_transaction_id было всегда разное (оно уникальное).