[php] 사람들이 PHP 기반의 플래시 게임의 최고 점수 테이블을 해킹하는 것을 막는 가장 좋은 방법은 무엇입니까

점수 상한이 없으며 동작 등을 재생하여 서버에서 점수를 확인할 수있는 액션 게임에 대해 이야기하고 있습니다.

내가 정말로 필요한 것은 Flash / PHP에서 가능한 가장 강력한 암호화와 사람들이 내 Flash 파일이 아닌 다른 PHP 페이지를 호출하지 못하게하는 방법입니다. 과거에는 단일 점수를 여러 번 호출하고 체크섬 / 피보나치 시퀀스 등을 완료하고 Amayeta SWF Encrypt를 사용하여 SWF를 난독 처리하는 간단한 방법을 시도했지만 결국 해킹당했습니다.

– StackOverflow의 반응 덕분에 지금은 어도비에서 몇 가지 추가 정보를 발견했다 http://www.adobe.com/devnet/flashplayer/articles/secure_swf_apps_12.htmlhttps://github.com/mikechambers/as3corelib – 내가 생각 암호화에 사용할 수 있습니다. 그래도 CheatEngine을 해결할 수 있는지 확실하지 않습니다.

AS2와 AS3 모두에 대해 최상의 솔루션을 알고 있어야합니다.

주요 문제는 TamperData 및 LiveHTTP 헤더와 같은 것으로 보이지만 CheatEngine (Mark Webster 덕분에)과 같은 고급 해킹 도구가 있음을 알고 있습니다



답변

이것은 인터넷 게임 및 콘테스트의 고전적인 문제입니다. Flash 코드는 사용자와 함께 게임 점수를 결정합니다. 그러나 사용자는 신뢰할 수 없으며 Flash 코드는 사용자의 컴퓨터에서 실행됩니다. 당신은 SOL입니다. 공격자가 높은 점수를 얻지 못하도록하기 위해 할 수있는 일은 없습니다 :

  • 바이트 코드가 잘 문서화되어 있고 고급 언어 (Actionscript)를 설명하기 때문에 Flash는 생각보다 리버스 엔지니어링이 훨씬 쉽습니다. 플래시 게임을 게시 할 때 소스 코드를 게시하고 있습니다. 알고 있든 없든

  • 공격자는 Flash 인터프리터의 런타임 메모리를 제어하므로 프로그래밍 가능한 디버거를 사용하는 방법을 알고있는 사람은 언제든지 변수 (현재 점수 포함)를 변경하거나 프로그램 자체를 변경할 수 있습니다.

시스템에 대한 가장 간단한 공격은 프록시를 통해 게임의 HTTP 트래픽을 실행하고 점수를 많이 저장 한 후 더 높은 점수로 재생하는 것입니다.

예를 들어 게임 시작시 클라이언트에 암호화 된 토큰을 전송하는 등의 각 최고 점수 저장을 게임의 단일 인스턴스에 바인딩하여이 공격을 차단할 수 있습니다.

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

(세션 쿠키를 같은 효과로 사용할 수도 있습니다).

게임 코드는이 토큰을 높은 점수 저장과 함께 서버로 에코합니다. 그러나 공격자는 여전히 게임을 다시 시작하고 토큰을 얻은 다음 해당 토큰을 즉시 재생 된 최고 점수 저장에 붙여 넣을 수 있습니다.

따라서 다음으로 토큰 또는 세션 쿠키뿐만 아니라 높은 점수의 암호화 세션 키도 제공합니다. 이것은 128 비트 AES 키이며, 플래시 게임에 하드 코드 된 키로 자체 암호화됩니다.

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과 일치하는지 확인합니다 (이 단계를 건너 뛰는 경우) , 해독은 단순히 임의의, 아마도 매우 높고 높은 점수를 생성합니다).

이제 공격자는 플래시 코드를 디 컴파일하고 AES 코드를 신속하게 찾아냅니다. AES 코드는 아픈 엄지 손가락처럼 튀어 나오지만 15 분 안에 메모리 검색 및 추적 프로그램으로 추적되지는 않습니다. 이 게임에 대한 내 점수는 666입니다. 메모리에서 666을 찾은 다음 해당 값에 해당하는 모든 작업을 포착하십시오. 세션 키를 사용하면 공격자가 플래시 코드를 실행할 필요조차 없습니다. 그녀는 게임 시작 토큰과 세션 키를 잡고 임의의 높은 점수를 다시 보낼 수 있습니다.

이제 대부분의 개발자가 포기하고 — 다음 몇 가지 방법으로 공격자와 엉망이되는 몇 달을 포기합니다.

  • XOR 조작으로 AES 키 스크램블

  • 키 바이트 배열을 키를 계산하는 함수로 교체

  • 바이너리 전체에 가짜 키 암호화 및 높은 점수 게시를 흩뿌립니다.

이것은 대부분 시간 낭비입니다. 말할 것도없이 SSL도 도움이되지 않습니다. SSL은 두 SSL 엔드 포인트 중 하나가 악한 경우 보호 할 수 없습니다.

고득점 사기를 실제로 줄일 수있는 몇 가지 사항은 다음과 같습니다.

  • 게임을 플레이하려면 로그인이 필요하고, 로그인으로 세션 쿠키를 생성하고, 동일한 세션에서 여러 개의 미해결 게임을 실행하거나 동일한 사용자에 대해 여러 개의 동시 세션을 허용하지 마십시오.

  • 가장 짧은 실제 게임보다 오래 지속되지 않은 게임 세션에서 높은 점수를 거부합니다 (보다 정교한 접근 방식을 위해서는 평균 게임 지속 시간보다 2 표준 편차 미만으로 지속되는 게임 세션에 대해 “정리”높은 점수를보십시오). 서버 측 게임 시간을 추적하고 있는지 확인하십시오.

  • 한 번 또는 두 번만 게임을 한 로그인에서 높은 점수를 거부하거나 격리하여 공격자가 생성하는 각 로그인에 대해 합리적인 모양의 게임 플레이를 “종이 추적”해야합니다.

  • “하트 비트”는 게임 플레이 중 점수를 매기므로 서버는 한 게임 플레이 동안 평생 동안 점수가 증가하는 것을 볼 수 있습니다. 합리적인 점수 곡선을 따르지 않는 높은 점수를 거부합니다 (예 : 0에서 999999로 점프).

  • 게임 플레이 중 “스냅 샷”게임 상태 (예 : 탄약의 양, 레벨에서의 위치 등)는 나중에 기록 된 중간 점수와 조정할 수 있습니다. 시작하기 위해이 데이터에서 이상을 탐지 할 방법이 없어도됩니다. 당신은 그것을 모으기 만하면, 물고기가 비린 것처럼 보이면 돌아가서 분석 할 수 있습니다.

  • 보안 검사 중 하나에 실패한 사용자의 계정을 비활성화합니다 (예 : 유효성 검사에 실패한 암호화 된 최고 점수를 제출 함).

여기서는 높은 점수의 사기 만 저지한다는 사실을 기억하십시오. 없습니다 아무것도 당신이 경우에 방지하기 위해 할 수있는가. 게임에 돈이 있으면 누군가가 당신이 생각 해낸 시스템을 물리 칠 것입니다. 목표는 이 공격 을 막는 것이 아닙니다 . 게임을 정말 잘하고 치는 것보다 공격을 더 비싸게 만드는 것입니다.


답변

당신은 잘못된 질문을 할 수 있습니다. 사람들이 최고 점수를 올리는 데 사용하는 방법에 중점을 둔 것처럼 보이지만 특정 방법을 차단하는 것은 지금까지만 진행되었습니다. TamperData에 대한 경험이 없으므로 말할 수 없습니다.

당신이 물어봐야 할 질문은 “제출 된 점수가 유효하고 확실한지 어떻게 확인할 수 있습니까?” 구체적인 방법은 게임에 따라 다릅니다. 매우 간단한 퍼즐 게임의 경우 특정 시작 상태 및 이동 상태와 함께 점수를 넘겨 종료 상태로 만든 다음 동일한 이동을 사용하여 서버 쪽에서 게임을 다시 실행할 수 있습니다. 명시된 점수가 계산 된 점수와 동일한 지 확인하고 일치하는 경우에만 점수를 수락하십시오.


답변

이 작업을 수행하는 쉬운 방법은 점수와 함께 최고 점수 값의 암호화 해시를 제공하는 것입니다. 예를 들어, HTTP GET을 통해 결과를 게시 할 때 :
http://example.com/highscores.php?score=500&checksum=0a16df3dc0301a36a34f9065c3ff8095

이 체크섬을 계산할 때는 공유 비밀을 사용해야합니다. 이 비밀은 네트워크를 통해 전송되지 않아야하지만 PHP 백엔드와 플래시 프론트 엔드 내에서 하드 코딩되어야합니다. 위의 체크섬은 ” secret ” 문자열 앞에 ” 500 “을 붙여 md5sum을 통해 실행 하여 만들어졌습니다 .

이 시스템은 사용자가 임의의 점수를 게시하는 것을 방지하지만 사용자가 이전에 계산 된 점수와 해시 조합을 다시 게시하는 “재생 공격”을 방지하지는 않습니다. 위의 예에서 점수 500은 항상 동일한 해시 문자열을 생성합니다. 해시 될 문자열에 추가 정보 (예 : 사용자 이름, 타임 스탬프 또는 IP 주소)를 통합하면 이러한 위험을 완화 할 수 있습니다. 이렇게해도 데이터가 재생되지는 않지만 한 번에 한 명의 사용자에게만 데이터 집합이 유효하게됩니다.

방지하기 위해 모든 발생에서 재생 공격을 시도 – 응답 시스템의 일부 유형은 다음과 같이 작성되어야 할 것이다 :

  1. 플래시 게임 ( “클라이언트”)은 http://example.com/highscores.php 의 HTTP GET을 매개 변수없이 수행합니다 . 이 페이지는 무작위로 생성 된 솔트 값과 공유 비밀과 결합 된 해당 솔트 값의 암호화 해시 라는 두 가지 값을 반환합니다 . 이 솔트 값은 보류중인 쿼리의 로컬 데이터베이스에 저장해야하며 1 분 후에 “만료”될 수 있도록 타임 스탬프가 연결되어 있어야합니다.
  2. 플래시 게임은 솔트 값을 공유 비밀과 결합하고 해시를 계산하여 이것이 서버에서 제공 한 것과 일치하는지 확인합니다. 이 단계는 솔트 값이 실제로 서버에 의해 생성되었는지 확인하므로 사용자가 솔트 값을 변경하는 것을 방지하기 위해 필요합니다.
  3. 플래시 게임은 솔트 값을 공유 비밀, 높은 점수 값 및 기타 관련 정보 (닉네임, ip, 타임 스탬프)와 결합하고 해시를 계산합니다. 그런 다음 솔트 값, 높은 점수 및 기타 정보와 함께 HTTP GET 또는 POST를 통해이 정보를 PHP 백엔드로 다시 보냅니다.
  4. 서버는 클라이언트와 동일한 방식으로 수신 된 정보를 결합하고 해시를 계산하여 클라이언트가 제공 한 정보와 일치하는지 확인합니다. 그런 다음 보류중인 쿼리 목록에 나열된대로 솔트 값이 여전히 유효한지 확인합니다. 이 두 조건이 모두 해당되는 경우 최고 점수를 최고 점수 테이블에 기록하고 서명 된 “성공”메시지를 클라이언트에 반환합니다. 또한 보류중인 쿼리 목록에서 솔트 값을 제거합니다.

공유 비밀이 사용자가 액세스 할 수있는 경우 위의 기술 중 하나의 보안이 손상됩니다.

대안으로, 클라이언트가 HTTPS를 통해 서버와 통신하도록하고 클라이언트가 사용자가 액세스 할 수있는 특정 인증 기관이 서명 한 인증서 만 신뢰하도록 미리 구성하여 이러한 앞뒤를 피할 수 있습니다. .


답변

나는 tpqf가 말한 것을 좋아하지만, 부정 행위가 발견 될 때 계정을 비활성화하는 대신 허니팟을 구현하여 로그인 할 때마다 해킹 된 점수를보고 트롤로 표시된 것으로 의심하지 않습니다. “phpBB MOD Troll”에 대한 Google을 사용하면 독창적 인 접근 방식이 나타납니다.


답변

허용 된 답변에서 tqbf는 점수 변수에 대한 메모리 검색을 할 수 있다고 언급합니다 ( “내 점수는 666이므로 메모리에서 숫자 666을 찾으십시오”).

이 문제를 해결할 방법이 있습니다. 여기에 수업이 있습니다 : http://divillysausages.com/blog/safenumber_and_safeint

기본적으로 점수를 저장할 객체가 있습니다. setter에서는 전달한 값에 임의의 숫자 (+ 및-)를 곱하고, getter에서는 저장된 값을 임의의 곱셈기로 나눠서 원래 값을 가져옵니다. 간단하지만 메모리 검색을 중지하는 데 도움이됩니다.

또한, 해킹에 대처할 수있는 여러 가지 방법에 대해 설명하는 PushButton 엔진 뒤에있는 일부 사람들의 비디오를 확인하십시오. http://zaa.tv/2010/12/the-art-of-hacking-flash- 게임 / . 그들은 수업의 배후에서 영감을 받았습니다.


답변

나는 일종의 해결 방법을 만들었습니다 … 점수가 증가하는 곳을주었습니다 (항상 +1 점수를 얻음). 먼저 임의의 숫자부터 계산하기 시작했습니다 (14라고 말하십시오). 점수를 표시 할 때 점수를 마이너스 14로 표시했습니다. 그래서 크래커가 예를 들어 20을 찾으면 찾을 수 없습니다 ( 메모리에 34가됩니다). 둘째, 다음 포인트가 무엇인지 알고 있기 때문에 … 다음 포인트 무엇인지에 대한 해시를 만들기 위해 어도비 암호화 라이브러리를 사용했습니다 .. 점수를 증분해야 할 때 증분 점수의 해시가 해시와 같은지 확인합니다. 크래커가 메모리의 포인트를 변경 한 경우 해시는 동일하지 않습니다. 나는 서버 측 검증을 수행하고 게임과 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
}

그런 다음 다른 가짜 변수와 함께 변수를 보내면 길에서 잃어 버릴 수 있습니다 … 작은 플래시 게임에는 많은 작업이지만 상이 관련된 곳에서는 욕심 많은 사람들이 있습니다. 도움이 필요하면 PM을 작성하십시오.

건배, 이코


답변

알려진 (개인) 가역 키를 사용하여 암호화하는 것이 가장 간단한 방법입니다. 나는 모두 AS에 있지 않으므로 어떤 종류의 암호화 공급자가 있는지 잘 모르겠습니다.

그러나 게임 길이 (다시 암호화) 및 클릭 수와 같은 변수를 포함 할 수 있습니다.

이와 같은 모든 것은 리버스 엔지니어링 될 있으므로 사람들을 향기에서 던지기 위해 많은 정크 데이터를 던지는 것을 고려하십시오.

편집 : 일부 PHP 세션에서도 척킹 할 가치가 있습니다. 그들이 게임 시작을 클릭하면 세션을 시작하고 (이 게시물에 대한 의견에서 알 수 있듯이) 시간을 기록하십시오. 그들이 점수를 제출하면 실제로 오픈 게임을 받았으며 너무 빨리 또는 너무 큰 점수를 제출하지 않았 음을 확인할 수 있습니다.

스칼라를 운동하여 초당 최대 득점이 몇 분인지 알 수 있습니다.

이러한 것들 중 어느 것도 우회 할 수는 없지만 사람들이 그것을 볼 수있는 플래시에는없는 논리를 갖는 것이 도움이 될 것입니다.