it-swarm-pt.tech

Qual é a melhor maneira de impedir que as pessoas invadam a tabela de recordes baseada em PHP de um jogo em Flash

Estou falando de um jogo de ação sem limite de pontuação superior e sem maneira de verificar a pontuação no servidor repetindo movimentos etc.

O que eu realmente preciso é da criptografia mais forte possível no Flash/PHP e uma maneira de impedir que as pessoas chamem a página PHP que não seja através do meu arquivo Flash. Tentei alguns métodos simples no passado de fazendo várias chamadas para uma única pontuação e completando uma sequência de soma de verificação/fibonacci etc., além de ofuscar o SWF com o Amayeta SWF Encrypt, mas todos foram hackeados eventualmente.

Graças às respostas do StackOverflow, agora encontrei mais informações da Adobe - http://www.Adobe.com/devnet/flashplayer/articles/secure_swf_apps_12.html e https: // github .com/mikechambers/as3corelib - que eu acho que posso usar para a criptografia. Não tenho certeza se isso vai me levar ao redor do CheatEngine.

Preciso conhecer as melhores soluções para o AS2 e o AS3, se forem diferentes.

Os principais problemas parecem ser coisas como os cabeçalhos TamperData e LiveHTTP, mas entendo que também existem ferramentas de hacking mais avançadas - como o CheatEngine (obrigado Mark Webster)

212
Iain

Este é um problema clássico com jogos e concursos na Internet. Seu código Flash trabalha com os usuários para decidir uma pontuação para um jogo. Mas os usuários não são confiáveis ​​e o código Flash é executado no computador do usuário. Você é SOL. Não há nada que você possa fazer para impedir que um invasor forje pontuações mais altas:

  • O Flash é ainda mais fácil de fazer a engenharia reversa do que você imagina, pois os bytecodes estão bem documentados e descrevem uma linguagem de alto nível (Actionscript) --- quando você publica um jogo em Flash, publica seu código-fonte, esteja você conheça ou não.

  • Os invasores controlam a memória de tempo de execução do interpretador Flash, para que qualquer pessoa que saiba usar um depurador programável possa alterar qualquer variável (incluindo a pontuação atual) a qualquer momento ou o próprio programa.

O ataque mais simples possível contra o seu sistema é executar o tráfego HTTP do jogo por meio de um proxy, capturar a pontuação mais alta e reproduzi-la com uma pontuação mais alta.

Você pode tentar bloquear esse ataque vinculando cada save de pontuação alta a uma única instância do jogo, por exemplo, enviando um token criptografado para o cliente na inicialização do jogo, que pode parecer:

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

(Você também pode usar um cookie de sessão para o mesmo efeito).

O código do jogo faz com que esse token retorne ao servidor com o salvamento mais alto. Mas um invasor ainda pode simplesmente iniciar o jogo novamente, obter um token e colá-lo imediatamente em um save de alta pontuação repetido.

Então, em seguida, você alimenta não apenas um token ou cookie de sessão, mas também uma chave de sessão com criptografia de alta pontuação. Essa será uma chave AES de 128 bits, ela própria criptografada com uma chave codificada no jogo Flash:

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

Agora, antes que o jogo publique a pontuação mais alta, ele descriptografa a chave da sessão de criptografia de alta pontuação, o que pode ser feita porque você codificou a chave de decodificação da chave de sessão de criptografia de alta pontuação no binário do Flash. Você criptografa a pontuação mais alta com essa chave descriptografada, juntamente com o hash SHA1 da pontuação mais alta:

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

O código PHP no servidor verifica o token para garantir que a solicitação veio de uma instância de jogo válida) e descriptografa a pontuação máxima criptografada, verificando se a pontuação máxima corresponde ao SHA1 do pontuação alta (se você pular esta etapa, a descriptografia simplesmente produzirá pontuações aleatórias, provavelmente muito altas e altas).

Portanto, agora o invasor descompila seu código Flash e encontra rapidamente o código AES, que se destaca como um polegar dolorido, embora, mesmo se não fosse, seria rastreado em 15 minutos com uma pesquisa de memória e um rastreador ("Eu sei minha pontuação neste jogo é 666, então vamos encontrar 666 na memória e capturar qualquer operação que toque nesse valor --- olha, o código de criptografia de alta pontuação! "). Com a chave da sessão, o invasor nem precisa executar o código Flash; ela pega um token de lançamento do jogo e uma chave de sessão e pode enviar de volta uma pontuação alta arbitrária.

Agora você está no ponto em que a maioria dos desenvolvedores simplesmente desiste - ou aproveite alguns meses para mexer com os invasores:

  • Embaralhar as teclas AES com operações XOR

  • Substituindo matrizes de bytes de chave por funções que calculam a chave

  • Espalhando criptografias de chaves falsas e postagens de alta pontuação em todo o binário.

Isso tudo é principalmente uma perda de tempo. Escusado será dizer que o SSL também não irá ajudá-lo; O SSL não pode protegê-lo quando um dos dois pontos de extremidade SSL é ruim.

Aqui estão algumas coisas que podem realmente reduzir a fraude de pontuação alta:

  • Exija um login para jogar, faça com que ele produza um cookie de sessão e não permita vários lançamentos pendentes de jogos na mesma sessão ou várias sessões simultâneas para o mesmo usuário.

  • Rejeite pontuações altas de sessões de jogos que duram menos que os jogos reais mais curtos já realizados (para uma abordagem mais sofisticada, tente "colocar em quarentena" altas pontuações para sessões de jogos que durem menos de 2 desvios padrão abaixo da duração média do jogo). Verifique se você está acompanhando as durações dos jogos no servidor.

  • Rejeite ou coloque em quarentena as pontuações mais altas dos logins que apenas jogaram o jogo uma ou duas vezes, para que os atacantes tenham que produzir uma "trilha de papel" de jogabilidade razoável para cada login que criarem.

  • Pontuações "Heartbeat" durante o jogo, para que seu servidor veja o crescimento da pontuação ao longo da vida de um jogo. Rejeite pontuações altas que não seguem curvas razoáveis ​​de pontuação (por exemplo, saltando de 0 a 999999).

  • Estado do jogo "Instantâneo" durante o jogo (por exemplo, quantidade de munição, posição no nível etc.), que você pode reconciliar posteriormente com as pontuações intermediárias registradas. Você nem precisa ter uma maneira de detectar anomalias nesses dados; você apenas precisa coletá-lo e, em seguida, pode voltar e analisá-lo se as coisas parecerem duvidosas.

  • Desabilite a conta de qualquer usuário que falhe em uma de suas verificações de segurança (por exemplo, sempre enviando uma pontuação alta criptografada que falha na validação).

Lembre-se, porém, de que você está apenas impedindo a fraude de pontuação alta aqui. Há nada que você pode fazer para impedir se. Se houver dinheiro em jogo no jogo, alguém derrotará qualquer sistema que você criar. O objetivo não é parar este ataque; é tornar o ataque mais caro do que ficar muito bom no jogo e derrotá-lo.

414
tqbf

Você pode estar fazendo a pergunta errada. Você parece focado nos métodos que as pessoas estão usando para subir na lista de recordes, mas o bloqueio de métodos específicos só vai tão longe. Não tenho experiência com o TamperData, então não posso falar sobre isso.

A pergunta que você deve fazer é: "Como posso verificar se as pontuações enviadas são válidas e autênticas?" A maneira específica de fazer isso depende do jogo. Para jogos de quebra-cabeça muito simples, você pode enviar a pontuação juntamente com o estado inicial específico e a sequência de movimentos que resultaram no estado final e, em seguida, executar novamente o jogo no lado do servidor usando os mesmos movimentos. Confirme se a pontuação declarada é a mesma que a pontuação calculada e só aceite a pontuação se corresponderem.

25
Stephen Deken

Uma maneira fácil de fazer isso seria fornecer um hash criptográfico do seu valor de pontuação máxima junto com a pontuação que ela própria. Por exemplo, ao postar os resultados via HTTP GET: http://example.com/highscores.php?score=500&checksum=0a16df3dc0301a36a34f9065c3ff8095

Ao calcular essa soma de verificação, um segredo compartilhado deve ser usado; esse segredo nunca deve ser transmitido pela rede, mas deve ser codificado dentro do back-end PHP e no front-end do flash. A soma de verificação acima foi criada com o prefixo da string " secret "para a pontuação" 500 "e executando-o no md5sum.

Embora esse sistema impeça um usuário de publicar pontuações arbitrárias, ele não impede um "ataque de repetição", em que um usuário repõe uma combinação de pontuação e hash calculada anteriormente. No exemplo acima, uma pontuação de 500 sempre produziria a mesma cadeia de hash. Alguns desses riscos podem ser atenuados incorporando mais informações (como nome de usuário, carimbo de data e hora ou endereço IP) na cadeia de caracteres a ser hash. Embora isso não impeça a reprodução de dados, ele garantirá que um conjunto de dados seja válido apenas para um único usuário ao mesmo tempo.

Para impedir qualquer ataque de repetição ocorra, algum tipo de sistema de resposta a desafios terá que ser criado, como o seguinte:

  1. O jogo flash ("o cliente") executa um HTTP GET de http://example.com/highscores.php sem parâmetros. Esta página retorna dois valores: um valor salt gerado aleatoriamente e um hash criptográfico desse valor de sal combinado com o segredo compartilhado. Esse valor salt deve ser armazenado em um banco de dados local de consultas pendentes e deve ter um registro de data e hora associado a ele para que possa "expirar" após um minuto.
  2. O jogo flash combina o valor salt com o segredo compartilhado e calcula um hash para verificar se isso corresponde ao fornecido pelo servidor. Esta etapa é necessária para evitar a violação dos valores de sal pelos usuários, pois verifica se o valor de sal foi realmente gerado pelo servidor.
  3. O jogo flash combina o valor do sal com o segredo compartilhado, o valor da pontuação mais alta e qualquer outra informação relevante (apelido, ip, carimbo de data e hora) e calcula um hash. Em seguida, envia essas informações de volta ao back-end PHP via HTTP GET ou POST, juntamente com o valor do sal, a pontuação mais alta e outras informações).
  4. O servidor combina as informações recebidas da mesma maneira que no cliente e calcula um hash para verificar se isso corresponde ao fornecido pelo cliente. Ele também verifica se o valor salt ainda é válido conforme listado na lista de consultas pendentes. Se essas duas condições forem verdadeiras, ele grava a pontuação mais alta na tabela de pontuação mais alta e retorna uma mensagem de "sucesso" assinada ao cliente. Ele também remove o valor salt da lista de consultas pendentes.

Lembre-se de que a segurança de qualquer uma das técnicas acima é comprometida se o segredo compartilhado estiver acessível ao usuário

Como alternativa, algumas dessas alternâncias podem ser evitadas forçando o cliente a se comunicar com o servidor por HTTPS e assegurando que o cliente esteja pré-configurado para confiar apenas nos certificados assinados por uma autoridade de certificação específica à qual você só tem acesso. .

18
stormlash

Eu gosto do que o tpqf disse, mas, em vez de desativar uma conta quando a fraude é descoberta, implemente um honeypot para que, sempre que eles façam login, vejam suas pontuações hackeadas e nunca suspeitem que foram marcados como trolls. Google para "phpBB MOD Troll" e você verá uma abordagem engenhosa.

11
DGM

Eu fiz uma espécie de solução alternativa ... Eu dei um ponto onde as pontuações eram incrementadas (você sempre obtém +1). Primeiro, comecei a contar a partir de um número aleatório (digamos 14) e, quando mostro as pontuações, mostrei as pontuações var menos 14. Isso foi assim, se os crackers estão procurando, por exemplo, 20, eles não a encontrarão ( será 34 na memória). Segundo, já que sei qual deve ser o próximo ponto ... Usei a biblioteca de criptografia da Adobe para criar o hash do que o próximo ponto deveria ser. Quando preciso incrementar as pontuações, verifico se o hash das pontuações incrementadas é igual ao hash que deve ser. Se o cracker alterou os pontos na memória, os hashes não são iguais. Realizo algumas verificações do lado do servidor e, quando obtive pontos diferentes do jogo e do PHP, sei que trapaceiros estavam envolvidos. Aqui está um trecho do meu código (estou usando a classe MD5 do Adobe Crypto libraty e o sal de criptografia aleatória. CallPhp () é a validação do meu servidor)

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");
            }
        }

Usando essa técnica + obfusão do SWF, consegui parar os crackers. Além disso, quando estou enviando as pontuações para o lado do servidor, uso minha própria função pequena de criptografia/descriptografia. Algo assim (código do lado do servidor não incluído, mas você pode ver o algoritmo e escrevê-lo em 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
}

Então eu envio a variável entre outros falsos vars e ela se perde no meio do caminho ... É muito trabalho apenas para pequenos jogos em flash, mas onde há prêmios envolvidos, algumas pessoas ficam gananciosas. Se precisar de ajuda, escreva-me um PM.

Saúde, Ico

Na resposta aceita, o tqbf menciona que você pode fazer uma pesquisa na memória para a variável score ("Minha pontuação é 666, então procuro o número 666 na memória").

Existe uma maneira de contornar isso. Eu tenho uma classe aqui: http://divillysausages.com/blog/safenumber_and_safeint

Basicamente, você tem um objeto para armazenar sua pontuação. No setter, ele multiplica o valor que você passa com um número aleatório (+ e -), e no getter, você divide o valor salvo pelo multiplicador aleatório para recuperar o original. É simples, mas ajuda a interromper a pesquisa de memória.

Além disso, confira o vídeo de alguns dos caras por trás do mecanismo PushButton que falam sobre algumas das diferentes maneiras de combater os hackers: http://zaa.tv/2010/12/the-art-of- hacking-flash-games / . Eles foram a inspiração por trás da classe.

4
divillysausages

Você não pode confiar em nenhum dado retornado pelo cliente. A validação precisa ser realizada no lado do servidor. Não sou desenvolvedor de jogos, mas faço software comercial. Em ambos os casos, o dinheiro pode estar envolvido e as pessoas quebram as técnicas de ofuscação do lado do cliente.

Talvez envie dados de volta ao servidor periodicamente e faça alguma validação. Não se concentre no código do cliente, mesmo que seja aí que o seu aplicativo mora.

3
zeller

Criptografar usando uma chave reversível (privada) conhecida seria o método mais simples. Eu não gosto muito do AS, então não tenho certeza de que tipos de provedores de criptografia existem.

Mas você pode incluir variáveis ​​como a duração do jogo (criptografada novamente) e a contagem de cliques.

Todas essas coisas podem têm engenharia reversa, então considere incluir um monte de dados indesejados para tirar as pessoas do cheiro.

Edit: Pode valer a pena jogar em algumas PHP sessões também. Inicie a sessão quando eles clicarem em iniciar jogo e (como diz o comentário deste post)) registre o tempo. Quando eles enviam a pontuação, você pode verificar se eles realmente têm um jogo aberto e se não estão enviando uma pontuação muito cedo ou muito grande.

Pode valer a pena elaborar um escalar para ver dizer qual é a pontuação máxima por segundo/minuto de jogo.

Nenhuma dessas coisas é incircunventável, mas ajudará a ter alguma lógica que não esteja no Flash, onde as pessoas possam vê-la.

3
Oli

Na minha experiência, isso é melhor abordado como um problema de engenharia social do que como um problema de programação. Em vez de se concentrar em tornar impossível trapacear, concentre-se em torná-lo chato, removendo os incentivos para trapacear. Por exemplo, se o principal incentivo for altas pontuações publicamente visíveis, simplesmente atrasar quando altas pontuações são mostradas pode reduzir significativamente as trapaças, removendo o ciclo de feedback positivo dos trapaceiros.

3
Scott Reynen

Você está falando sobre o que é chamado de problema "confiança do cliente". Porque o cliente (nesse dinheiro, um SWF sendo executado em um navegador) está fazendo algo para o qual foi projetado. Salve uma pontuação alta.

O problema é que você deseja garantir que as solicitações de "salvar pontuação" sejam provenientes do seu filme em flash, e não alguma solicitação HTTP arbitrária. Uma solução possível para isso é codificar um token gerado pelo servidor no SWF no momento da solicitação (usando flasm ) que deve acompanhar a solicitação para salvar uma pontuação alta. Depois que o servidor salvar essa pontuação, o token expirará e não poderá mais ser usado para solicitações.

A desvantagem disso é que um usuário poderá enviar apenas uma pontuação alta por carga do filme em flash - você precisará forçá-lo a atualizar/recarregar o SWF antes que ele possa reproduzir novamente para uma nova pontuação.

2
Peter Bailey

A maneira que um novo mod de arcade popular faz é enviar dados do flash para o php, de volta para o flash (ou recarregá-lo) e depois de volta para o php. Isso permite que você faça o que quiser para comparar os dados, além de ignorar os hacks pós-descriptografia/dados e afins. Uma maneira de fazer isso é atribuindo 2 valores aleatórios de php no flash (que você não pode capturar ou ver, mesmo executando um captador de dados em flash em tempo real), usando uma fórmula matemática para adicionar a pontuação aos valores aleatórios e verificando-a usando a mesma fórmula para invertê-lo para ver se a pontuação corresponde quando finalmente chega ao php no final. Esses valores aleatórios nunca são visíveis, assim como também cronometra a transação e, se demorar mais de alguns segundos, também o sinaliza como trapaça, pois pressupõe que você interrompeu o envio para tentar descobrir os valores aleatórios ou executar os números através de algum tipo de cifra para retornar possíveis valores aleatórios para comparar com o valor da pontuação.

Parece uma solução muito boa, se você me perguntar, alguém vê algum problema ao usar esse método? Ou possíveis maneiras de contornar isso?

2
Vaughn

Eu acho que a maneira mais simples seria fazer chamadas para uma função como RegisterScore (score) cada vez que o jogo registra uma pontuação a ser adicionada e depois a codifica, empacota e envia para um script php como uma string. O script php saberia decodificá-lo corretamente. Isso interromperia todas as chamadas diretamente para o script php, pois qualquer tentativa de forçar uma pontuação resultaria em um erro de descompressão.

2
mathieu landry

Sempre que seu sistema de recordes se baseia no fato de que o aplicativo Flash envia dados de recordes não criptografados/não assinados pela rede, eles podem ser interceptados e manipulados/reproduzidos. A resposta segue: criptografar (decentemente!) Ou assinar criptograficamente os dados de maior pontuação. Isso, pelo menos, torna mais difícil para as pessoas violarem o seu sistema de recordes porque elas precisam extrair a chave secreta do seu arquivo SWF. Muitas pessoas provavelmente desistirão ali. Por outro lado, basta uma pessoa individual extrair a chave e publicá-la em algum lugar.

As soluções reais envolvem mais comunicação entre o aplicativo Flash e o banco de dados de recordes, para que este possa verificar se uma determinada pontuação é um pouco realista. Isso provavelmente é complicado, dependendo do tipo de jogo que você tem.

2
Jan Krüger

Não há como torná-lo completamente inalterável, pois é fácil descompilar SWFs, e um hacker de desenvolvedor qualificado pode rastrear seu código e descobrir como ignorar qualquer sistema criptografado que você possa empregar.

Se você simplesmente deseja impedir que crianças trapaceiem usando ferramentas simples como o TamperData, pode gerar uma chave de criptografia que passa para o SWF na inicialização. Em seguida, use algo como http://code.google.com/p/as3crypto/ para criptografar a pontuação mais alta antes de repassá-la para o código PHP. PHP, em seguida, descriptografar no final do servidor antes de armazená-lo no banco de dados.

2
David Arno

Normalmente, incluo "dados fantasmas" da sessão do jogo com a entrada de recordes. Então, se estou fazendo um jogo de corrida, incluo os dados de repetição. Você já tem os dados de repetição para funcionalidade de repetição ou de corrida fantasma (jogando contra sua última corrida ou contra o fantasma do cara n ° 14 na tabela de classificação).

Verificar isso é muito trabalhoso, mas se o objetivo é verificar se as 10 principais entradas de um concurso são legítimas, isso pode ser uma adição útil ao arsenal de medidas de segurança que outros já apontaram.

Se o objetivo é manter a lista de recordes on-line até o final dos tempos, sem que ninguém precise olhar para eles, isso não lhe trará muito.

2
Lucas Meijer

Só é possível mantendo o toda a lógica do jogo no lado do servidor que também armazena a pontuação internamente sem o conhecimento do usuário. Por razões econômicas e científicas, a humanidade não pode aplicar essa teoria a todos os tipos de jogos, exceto baseados em turnos. Por ex. manter a física no lado do servidor é computacionalmente caro e difícil de responder com rapidez. Mesmo possível, enquanto estiver jogando xadrez, qualquer um pode combinar a jogabilidade de IA com o oponente. Portanto, melhor jogos multiplayer também deve conter criatividade sob demanda.

2
placeohlder

Não é realmente possível alcançar o que você deseja. As partes internas do aplicativo Flash são sempre parcialmente acessíveis, especialmente quando você sabe usar coisas como CheatEngine , o que significa que, por mais seguro que o seu site e o navegador <-> as comunicações do servidor sejam, ele ainda será ser relativamente simples de superar.

1
Mark Webster

Pode ser uma boa idéia se comunicar com o back-end via AMFPHP . Isso deve desencorajar pelo menos os preguiçosos de tentar enviar os resultados pelo console do navegador.

1
m1gu3l