Как лучше всего помешать людям взломать таблицу рекордов флэш-игры на основе PHP?

StackOverflow https://stackoverflow.com/questions/73947

Вопрос

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

Что мне действительно нужно, так это самое сильное шифрование, возможное во Flash/PHP, и способ запретить людям вызывать страницу PHP иначе, чем через мой Flash-файл.В прошлом я пробовал несколько простых методов: выполнять несколько вызовов для одной оценки и завершать контрольную сумму / последовательность Фибоначчи и т. д., а также запутывать SWF с помощью Amayeta SWF Encrypt, но в конечном итоге все они были взломаны.

Благодаря ответам StackOverflow я нашел дополнительную информацию от Adobe – http://www.adobe.com/devnet/flashplayer/articles/secure_swf_apps_12.html и https://github.com/mikechambers/as3corelib - который, я думаю, я могу использовать для шифрования.Не уверен, что это поможет мне разобраться с CheatEngine.

Мне нужно знать лучшие решения для AS2 и AS3, если они разные.

Основные проблемы, похоже, связаны с такими вещами, как TamperData и заголовки LiveHTTP, но я понимаю, что есть и более продвинутые инструменты взлома, такие как CheatEngine (спасибо Марку Вебстеру).

Это было полезно?

Решение

Это классическая проблема интернет-игр и конкурсов.Ваш Flash-код помогает пользователям определить счет в игре.Но пользователям не доверяют, и Flash-код запускается на компьютере пользователя.Ты СОЛ.Вы ничего не можете сделать, чтобы помешать злоумышленнику подделать высокие баллы:

  • Реконструировать Flash еще проще, чем вы думаете, поскольку байт-коды хорошо документированы и описывают язык высокого уровня (Actionscript). Когда вы публикуете Flash-игру, вы публикуете свой исходный код, независимо от того, хотите ли вы знаешь это или нет.

  • Злоумышленники контролируют рабочую память интерпретатора Flash, так что любой, кто знает, как использовать программируемый отладчик, может в любое время изменить любую переменную (включая текущий результат) или изменить саму программу.

Самая простая возможная атака на вашу систему — пропустить HTTP-трафик игры через прокси, перехватить сохранение с высоким результатом и воспроизвести его с более высоким результатом.

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

hex-encoding( AES(secret-key-stored-only-on-server, timestamp, user-id, random-number))

(Для того же эффекта вы также можете использовать сеансовый файл cookie).

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

Итак, затем вы передаете не только токен или файл cookie сеанса, но также ключ сеанса с высоким уровнем шифрования.Это будет 128-битный ключ AES, зашифрованный ключом, жестко запрограммированным во Flash-игре:

hex-encoding( AES(key-hardcoded-in-flash-game, random-128-bit-key))

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

hex-encoding( AES(random-128-bit-key-from-above, high-score, SHA1(high-score)))

Код PHP на сервере проверяет токен, чтобы убедиться, что запрос поступил от действительного экземпляра игры, затем расшифровывает зашифрованный рекорд, проверяя, соответствует ли рекорд SHA1 рекорда (если вы пропустите этот шаг , расшифровка просто даст случайные, вероятно, очень высокие баллы).

Итак, теперь злоумышленник декомпилирует ваш Flash-код и быстро находит AES-код, который торчит как больной палец, хотя даже если бы это было не так, его бы отследили за 15 минут с помощью поиска в памяти и трассировщика («Я знаю мой результат в этой игре — 666, так что давайте найдем 666 в памяти, а затем перехватим любую операцию, касающуюся этого значения — о, посмотрите, код шифрования с самым высоким баллом!»).Благодаря сеансовому ключу злоумышленнику даже не нужно запускать Flash-код;она берет токен запуска игры и сеансовый ключ и может отправить обратно произвольный высокий балл.

Сейчас вы находитесь в той точке, когда большинство разработчиков просто сдаются — плюс-минус пара месяцев возни с злоумышленниками:

  • Скремблирование ключей AES с помощью операций XOR

  • Замена массивов ключевых байтов функциями, вычисляющими ключ

  • Распространение поддельных ключей шифрования и сообщений о высоких баллах по всему двоичному файлу.

По большей части это все пустая трата времени.Само собой разумеется, SSL вам тоже не поможет;SSL не сможет защитить вас, если одна из двух конечных точек SSL является вредоносной.

Вот некоторые вещи, которые действительно могут снизить уровень мошенничества с высокими оценками:

  • Требовать вход для входа в игру, создавать файл cookie сеанса при входе в систему и не разрешать несколько невыполненных запусков игры в одном сеансе или несколько одновременных сеансов для одного и того же пользователя.

  • Отклоняйте высокие результаты в игровых сессиях, которые длятся меньше, чем самые короткие реальные игры, когда-либо сыгранные (для более сложного подхода попробуйте «поместить в карантин» высокие результаты для игровых сессий, которые длятся менее чем на 2 стандартных отклонения ниже средней продолжительности игры).Убедитесь, что вы отслеживаете продолжительность игры на сервере.

  • Отклонять или помещать в карантин высокие баллы от пользователей, которые играли в игру всего один или два раза, так что злоумышленникам придется составлять «бумажный след» разумного игрового процесса для каждого созданного ими входа.

  • «Heartbeat» набирает очки во время игры, поэтому ваш сервер видит рост очков в течение одной игры.Отклоняйте высокие баллы, которые не соответствуют разумным кривым оценок (например, скачок от 0 до 999999).

  • «Моментальный снимок» состояния игры во время игры (например, количество боеприпасов, положение на уровне и т. д.), которое позже можно сверить с записанными промежуточными результатами.Для начала вам даже не обязательно иметь способ обнаружить аномалии в этих данных;вам просто нужно собрать их, а затем вернуться и проанализировать, если что-то выглядит подозрительно.

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

Однако помните, что здесь вы всего лишь сдерживаете мошенничество с крупными счетами.Есть ничего вы можете сделать, чтобы предотвратить, если.Если в вашей игре на кону стоят деньги, кто-то обязательно победит любую систему, которую вы придумаете.Цель состоит не в том, чтобы останавливаться эта атака;это сделать атаку более дорогостоящей, чем просто научиться хорошо играть и победить ее.

Другие советы

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

Вопрос, который вы должны задать:«Как я могу проверить, что представленные оценки являются действительными и подлинными?» Конкретный способ сделать это зависит от игры.В очень простых играх-головоломках вы можете отправить счет вместе с конкретным начальным состоянием и последовательностью ходов, которые привели к конечному состоянию, а затем повторно запустить игру на стороне сервера, используя те же ходы.Убедитесь, что заявленная оценка совпадает с вычисленной, и принимайте оценки только в том случае, если они совпадают.

Самый простой способ сделать это — предоставить криптографический хэш вашего рекорда вместе с самим результатом.Например, при публикации результатов через HTTP GET:http://example.com/highscores.php?score=500&checksum=0a16df3dc0301a36a34f9065c3ff8095

При вычислении этой контрольной суммы следует использовать общий секрет;этот секрет никогда не следует передавать по сети, он должен быть жестко запрограммирован как в PHP-сервере, так и во флэш-интерфейсе.Контрольная сумма выше была создана путем добавления строки "секрет"на счет"500", и запускаем его через md5sum.

Хотя эта система не позволяет пользователю публиковать произвольные оценки, она не предотвращает «атаку повторного воспроизведения», когда пользователь повторно публикует ранее рассчитанную комбинацию очков и хеша.В приведенном выше примере оценка 500 всегда будет давать одну и ту же хеш-строку.Частично этот риск можно снизить, включив дополнительную информацию (например, имя пользователя, метку времени или IP-адрес) в строку, подлежащую хешированию.Хотя это не предотвратит воспроизведение данных, оно будет гарантировать, что набор данных будет действителен только для одного пользователя в один момент времени.

Предотвращать любой Чтобы не повторять атаки, необходимо будет создать некую систему вызова-ответа, например следующую:

  1. Флэш-игра («клиент») выполняет HTTP GET http://example.com/highscores.php без параметров.Эта страница возвращает два значения:случайно сгенерированный соль значение и криптографический хэш этого значения соли в сочетании с общим секретом.Это значение соли должно храниться в локальной базе данных ожидающих запросов и должно иметь связанную с ним временную метку, чтобы срок его действия истекал примерно через одну минуту.
  2. Флеш-игра объединяет значение соли с общим секретом и вычисляет хэш, чтобы убедиться, что он соответствует значению, предоставленному сервером.Этот шаг необходим для предотвращения изменения значений соли пользователями, поскольку он проверяет, действительно ли значение соли было сгенерировано сервером.
  3. Флэш-игра объединяет значение соли с общим секретом, значением рекорда и любой другой соответствующей информацией (псевдоним, IP-адрес, метка времени) и вычисляет хэш.Затем он отправляет эту информацию обратно в серверную часть PHP через HTTP GET или POST вместе со значением соли, высоким баллом и другой информацией.
  4. Сервер объединяет полученную информацию так же, как на клиенте, и вычисляет хэш, чтобы убедиться, что он соответствует тому, который предоставил клиент.Затем он также проверяет, что значение соли по-прежнему действительно, как указано в списке ожидающих запросов.Если оба этих условия верны, он записывает высокий балл в таблицу рекордов и возвращает клиенту подписанное сообщение об успехе.Он также удаляет значение соли из списка ожидающих запросов.

Имейте в виду, что безопасность любого из вышеперечисленных методов будет поставлена ​​под угрозу, если общий секрет когда-либо станет доступен пользователю.

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

Мне нравится то, что сказал tpqf, но вместо того, чтобы отключать учетную запись при обнаружении мошенничества, внедрите приманку, чтобы всякий раз, когда они входят в систему, они видели свои взломанные результаты и никогда не подозревали, что их пометили как тролля.Погуглите «phpBB MOD Troll», и вы увидите гениальный подход.

В принятом ответе tqbf упоминает, что вы можете просто выполнить поиск в памяти переменной оценки («Мой счет 666, поэтому я ищу число 666 в памяти»).

Есть способ обойти это.У меня есть класс здесь: http://divillysausages.com/blog/safenumber_and_safeint

По сути, у вас есть объект для хранения вашего счета.В установщике он умножает переданное вами значение на случайное число (+ и -), а в геттере вы делите сохраненное значение на случайный множитель, чтобы получить обратно исходное значение.Это просто, но помогает остановить поиск в памяти.

Также посмотрите видео от ребят, создавших движок PushButton, которые рассказывают о различных способах борьбы со взломом: http://zaa.tv/2010/12/the-art-of-hacking-flash-games/.Они были вдохновением для класса.

Я сделал своего рода обходной путь...У меня был случай, когда баллы увеличивались (вы всегда получаете +1 балл).Сначала я начал считать со случайного числа (скажем, 14), и когда я отображал баллы, он просто показывал баллы var минус 14.Это было так, если взломщики будут искать например 20, то не найдут (в памяти будет 34).Во-вторых, поскольку я знаю, каким должен быть следующий пункт...Я использовал криптографическую библиотеку Adobe, чтобы создать хеш следующего пункта. должно быть.Когда мне нужно увеличить баллы, я проверяю, равен ли хеш увеличенных баллов тому хешу, который должен быть.Если взломщик изменил точки в памяти, хеши не равны.Я выполняю некоторую проверку на стороне сервера, и когда я получаю разные баллы от игры и от PHP, я знаю, что имел место обман.Вот фрагмент моего кода (я использую класс Adobe Crypto libraty MD5 и случайную криптографическую соль.callPhp() — это проверка на стороне моего сервера)

private function addPoint(event:Event = null):void{
            trace("expectedHash: " + expectedHash + "  || new hash: " + MD5.hash( Number(SCORES + POINT).toString() + expectedHashSalt) );
            if(expectedHash == MD5.hash( Number(SCORES + POINT).toString() + expectedHashSalt)){
                SCORES +=POINT;
                callPhp();
                expectedHash = MD5.hash( Number(SCORES + POINT).toString() + expectedHashSalt);
            } else {
                //trace("cheat engine usage");
            }
        }

Используя эту технику + обфускацию SWF, мне удалось остановить взломщиков.Кроме того, когда я отправляю результаты на сервер, я использую свою собственную небольшую функцию шифрования/дешифрования.Примерно так (серверный код не включен, но вы можете увидеть алгоритм и написать его на PHP):

package  {

    import bassta.utils.Hash;

    public class ScoresEncoder {

        private static var ranChars:Array;
        private static var charsTable:Hash;

        public function ScoresEncoder() {

        }

        public static function init():void{

            ranChars = String("qwertyuiopasdfghjklzxcvbnm").split("")

            charsTable = new Hash({
                "0": "x",
                "1": "f",
                "2": "q",
                "3": "z",
                "4": "a",
                "5": "o",
                "6": "n",
                "7": "p",
                "8": "w",
                "9": "y"

            });

        }

        public static function encodeScore(_s:Number):String{

            var _fin:String = "";

            var scores:String = addLeadingZeros(_s);
            for(var i:uint = 0; i< scores.length; i++){
                //trace( scores.charAt(i) + " - > " + charsTable[ scores.charAt(i) ] );
                _fin += charsTable[ scores.charAt(i) ];
            }

            return _fin;

        }

        public static function decodeScore(_s:String):String{

            var _fin:String = "";

            var decoded:String = _s;

            for(var i:uint = 0; i< decoded.length; i++){
                //trace( decoded.charAt(i) + " - > "  + charsTable.getKey( decoded.charAt(i) ) );
                _fin += charsTable.getKey( decoded.charAt(i) );
            }

            return _fin;

        }

        public static function encodeScoreRand(_s:Number):String{
            var _fin:String = "";

            _fin += generateRandomChars(10) + encodeScore(_s) + generateRandomChars(3)

            return _fin;
        }

        public static function decodeScoreRand(_s:String):Number{

            var decodedString:String = _s;
            var decoded:Number;

            decodedString = decodedString.substring(10,13);         
            decodedString = decodeScore(decodedString);

            decoded = Number(decodedString);

            return decoded;
        }

        public static function generateRandomChars(_length:Number):String{

            var newRandChars:String = "";

            for(var i:uint = 0; i< _length; i++){
                newRandChars+= ranChars[ Math.ceil( Math.random()*ranChars.length-1 )];
            }

            return newRandChars;
        }

        private static function addLeadingZeros(_s:Number):String{

            var _fin:String;

            if(_s < 10 ){
                 _fin = "00" + _s.toString();
            }

            if(_s >= 10 && _s < 99 ) {
                 _fin = "0" + _s.toString();
            }

            if(_s >= 100 ) {
                _fin = _s.toString();
            }           

            return _fin;
        }


    }//end
}

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

Приветствую, Ико

Шифрование с использованием известного (частного) обратимого ключа было бы самым простым методом.Я не совсем разбираюсь в AS, поэтому не уверен, какие существуют поставщики шифрования.

Но вы можете включить такие переменные, как продолжительность игры (опять же зашифрованная) и количество кликов.

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

Редактировать:Возможно, стоит также отказаться от некоторых сеансов PHP.Начните сеанс, когда они нажмут кнопку «Начать игру», и (как сказано в комментарии к этому сообщению) зарегистрируйте время.Когда они отправят счет, вы можете убедиться, что у них действительно есть открытая игра, и они не отправляют счет слишком рано или слишком сильно.

Возможно, стоит разработать скаляр, чтобы узнать, каков максимальный балл за секунду/минуту игры.

Ни одну из этих вещей нельзя обойти, но полезно иметь некоторую логику не во Flash, где люди смогут ее увидеть.

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

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

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

Всякий раз, когда ваша система рекордов основана на том факте, что приложение Flash отправляет незашифрованные/неподписанные данные рекордов через сеть, они могут быть перехвачены и манипулированы/воспроизведены.Отсюда следует ответ:зашифруйте (прилично!) или криптографически подпишите данные рекордов.По крайней мере, это усложнит взлом вашей системы рекордов, поскольку им придется извлечь секретный ключ из вашего SWF-файла.Многие люди, вероятно, тут же сдадутся.С другой стороны, достаточно одного человека, чтобы извлечь ключ и разместить его где-нибудь.

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

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

Если вы просто хотите остановить мошенничество детей с помощью простых инструментов, таких как TamperData, вы можете создать ключ шифрования, который вы передаете SWF при запуске.Затем используйте что-то вроде http://code.google.com/p/as3crypto/ чтобы зашифровать рекорд перед передачей его обратно в код PHP.Затем расшифруйте его на стороне сервера перед сохранением в базе данных.

Вы говорите о так называемой проблеме «доверия клиента».Потому что клиент (в данном случае SWF, работающий в браузере) делает то, для чего он предназначен.Сохранить высокий балл.

Проблема в том, что вы хотите убедиться, что запросы «сохранить оценку» исходят из вашего флэш-фильма, а не из какого-то произвольного HTTP-запроса.Возможным решением этой проблемы является кодирование токена, сгенерированного сервером, в SWF во время запроса (используя плазма), которое должно сопровождать запрос на сохранение высокого балла.Как только сервер сохранит этот результат, срок действия токена истекает и его больше нельзя использовать для запросов.

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

Обычно я включаю «призрачные данные» игровой сессии в запись рекордов.Поэтому, если я делаю гоночную игру, я включаю данные повторов.Часто у вас уже есть данные повтора для функции повтора или функции призрачных гонок (игра против вашей последней гонки или игра против призрака чувака № 14 в таблице лидеров).

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

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

Новый популярный аркадный мод делает это следующим образом: он отправляет данные из флэш-памяти в php, обратно во флэш-память (или перезагружает ее), а затем обратно в php.Это позволяет вам делать все, что вы хотите, сравнивать данные, а также обходить хаки пост-данных/дешифрования и тому подобное.Один из способов сделать это — назначить 2 рандомизированных значения из php во флэш-память (которые вы не сможете получить или увидеть, даже если запустите сборщик флэш-данных в реальном времени), используя математическую формулу для сложения оценки со случайными значениями, а затем проверив ее с помощью та же формула, чтобы перевернуть ее, чтобы увидеть, соответствует ли оценка ей, когда она наконец перейдет на php в конце.Эти случайные значения никогда не видны, а также время выполнения транзакции, и если она превышает пару секунд, это также помечается как мошенничество, поскольку предполагает, что вы остановили отправку, чтобы попытаться выяснить рандомизированные значения или запустить числа через какой-то тип шифрования, чтобы вернуть возможные случайные значения для сравнения со значением оценки.

Это кажется довольно хорошим решением, если вы спросите меня, видит ли кто-нибудь какие-либо проблемы с использованием этого метода?Или возможные способы обойти это?

Я думаю, что самым простым способом было бы вызывать функцию типа RegisterScore(score) каждый раз, когда игра регистрирует добавляемый балл, а затем закодировать его, упаковать и отправить в php-скрипт в виде строки.PHP-скрипт знает, как правильно его декодировать.Это остановит любые вызовы непосредственно к php-скрипту, поскольку любые попытки принудительного получения оценки приведут к ошибке распаковки.

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

Невозможно достичь того, чего вы хотите.Внутренние элементы приложения Flash всегда частично доступны, особенно если вы знаете, как использовать такие вещи, как CheatEngine, Это означает, что независимо от того, насколько безопасен ваш веб-сайт и связь между браузером и сервером, преодолеть его все равно будет относительно легко.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top