it-swarm-es.tech

¿Cuál es la mejor manera de evitar que las personas pirateen la tabla de mejores puntuaciones basada en PHP de un juego Flash?

Estoy hablando de un juego de acción sin límite superior de puntuación y no hay forma de verificar la puntuación en el servidor mediante la repetición de movimientos, etc.

Lo que realmente necesito es el cifrado más sólido posible en Flash/PHP, y una forma de evitar que las personas llamen a la página PHP a través de mi archivo Flash. He intentado algunos métodos sencillos en el pasado de realizar varias llamadas para obtener una única puntuación y completar una secuencia de comprobación/fibonacci, etc., y también ofuscar el SWF con Amayeta SWF Encrypt, pero finalmente todos fueron pirateados.

Gracias a las respuestas de StackOverflow, ahora he encontrado más información de Adobe - http://www.Adobe.com/devnet/flashplayer/articles/secure_swf_apps_12.html y https://github.com/mikechambers/ as3corelib - que creo que puedo usar para el cifrado. Aunque no estoy seguro de que esto me ayude a manejar CheatEngine.

Necesito conocer las mejores soluciones tanto para AS2 como para AS3, si son diferentes.

Los principales problemas parecen ser cosas como los encabezados TamperData y LiveHTTP, pero entiendo que también existen herramientas de hacking más avanzadas, como CheatEngine (gracias Mark Webster)

212
Iain

Este es un problema clásico con los juegos y concursos de Internet. Tu código Flash funciona con los usuarios para decidir la puntuación de un juego. Pero los usuarios no son de confianza, y el código de Flash se ejecuta en la computadora del usuario. Eres sol No hay nada que puedas hacer para evitar que un atacante falsifique puntuaciones altas:

  • Flash es incluso más fácil de usar ingeniería inversa de lo que podría pensar, ya que los bytecodes están bien documentados y describen un lenguaje de alto nivel (Actionscript) --- cuando publicas un juego Flash, estás publicando tu código fuente, ya sea que lo se o no.

  • Los atacantes controlan la memoria de tiempo de ejecución del intérprete de Flash, de modo que cualquier persona que sepa usar un depurador programable puede alterar cualquier variable (incluida la puntuación actual) en cualquier momento, o alterar el programa en sí.

El ataque más simple posible contra tu sistema es ejecutar el tráfico HTTP para el juego a través de un proxy, capturar el guardado de puntuación más alta y volver a jugarlo con una puntuación más alta.

Puedes intentar bloquear este ataque vinculando cada partida de puntuación más alta a una única instancia del juego, por ejemplo, enviando un token cifrado al cliente al inicio del juego, que podría tener el siguiente aspecto:

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

(También puede utilizar una cookie de sesión para el mismo efecto).

El código del juego devuelve este token al servidor con el guardado de puntuación más alta. Pero un atacante aún puede volver a lanzar el juego, obtener un token y pegarlo inmediatamente en una partida de puntuación alta repetida.

De modo que, a continuación, alimenta no solo un token o una cookie de sesión, sino también una clave de sesión de cifrado de alta puntuación. Esta será una clave AES de 128 bits, en sí misma encriptada con una clave codificada en el juego Flash:

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

Ahora, antes de que el juego publique la puntuación más alta, descifra la clave de la sesión de cifrado de la puntuación más alta, lo que puede hacer porque usted codificó la clave de descifrado de la clave de la sesión de cifrado de la puntuación alta en el binario Flash. Cifras la puntuación más alta con esta clave descifrada, junto con el hash SHA1 de la puntuación más alta:

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

El código PHP en el servidor comprueba el token para asegurarse de que la solicitud proviene de una instancia de juego válida, luego descifra la puntuación más alta encriptada y verifica que la puntuación más alta coincida con la SHA1 de la puntuación más alta (Si omite este paso, el descifrado simplemente producirá puntuaciones altas aleatorias, probablemente muy altas).

Así que ahora el atacante descompila su código de Flash y encuentra rápidamente el código AES, que sobresale como un pulgar adolorido, aunque incluso si no fuera así, sería rastreado en 15 minutos con una búsqueda de memoria y un marcador ("Lo sé mi puntaje para este juego es 666, así que busquemos 666 en la memoria, luego capturemos cualquier operación que toque ese valor --- oh mira, ¡el código de encriptación de puntaje alto! "). Con la clave de sesión, el atacante ni siquiera tiene que ejecutar el código Flash; ella toma un token de lanzamiento del juego y una clave de sesión y puede devolver una puntuación alta arbitraria.

Ahora estás en el punto en el que la mayoría de los desarrolladores simplemente se dan por vencidos: da o toma un par de meses de meterse con los atacantes al:

  • Codificación de las claves AES con XOR operaciones

  • Reemplazo de matrices de bytes con funciones que calculan la clave

  • Dispersando cifrados de clave falsos y publicaciones de puntaje alto en todo el binario.

Todo esto es principalmente una pérdida de tiempo. No hace falta decir que SSL tampoco te va a ayudar; SSL no puede protegerlo cuando uno de los dos puntos finales de SSL es malo.

Aquí hay algunas cosas que realmente pueden reducir el fraude de puntaje alto:

  • Requerir un inicio de sesión para jugar, hacer que el inicio de sesión produzca una cookie de sesión y no permitir múltiples lanzamientos de juegos pendientes en la misma sesión o varias sesiones simultáneas para el mismo usuario.

  • Rechace los puntajes altos de las sesiones de juego que duran menos que los juegos reales más cortos que se hayan jugado (para un enfoque más sofisticado, intente los puntajes altos de "cuarentena" para las sesiones de juego que duren menos de 2 desviaciones estándar por debajo de la duración media del juego). Asegúrate de estar siguiendo las duraciones del juego en el servidor.

  • Rechazar o poner en cuarentena las puntuaciones altas de los inicios de sesión que solo han jugado el juego una o dos veces, por lo que los atacantes tienen que producir un "rastro de papel" de juego de aspecto razonable para cada inicio de sesión que crean.

  • Puntuaciones de "Heartbeat" durante el juego, de modo que su servidor vea el crecimiento de la puntuación a lo largo de la vida de un juego. Rechace puntuaciones altas que no sigan curvas de puntuación razonables (por ejemplo, saltando de 0 a 999999).

  • Estado de juego "instantáneo" durante el juego (por ejemplo, cantidad de municiones, posición en el nivel, etc.), que luego puede reconciliar con las puntuaciones provisionales registradas. Ni siquiera tiene que tener una forma de detectar anomalías en estos datos para comenzar; solo tienes que recogerlo, y luego puedes retroceder y analizarlo si las cosas se ven mal.

  • Deshabilite la cuenta de cualquier usuario que suspenda una de sus comprobaciones de seguridad (por ejemplo, enviando una puntuación alta encriptada que no supere la validación).

Sin embargo, recuerde que aquí solo está disuadiendo el fraude de alta puntuación. Hay nada puedes hacer para prevenir si. Si hay dinero en juego en tu juego, alguien derrotará cualquier sistema que encuentres. El objetivo no es detener este ataque; es hacer que el ataque sea más costoso que simplemente volverse realmente bueno en el juego y vencerlo.

414
tqbf

Usted puede estar haciendo la pregunta incorrecta. Pareces enfocado en los métodos que la gente está usando para subir en la lista de puntuación más alta, pero el bloqueo de métodos específicos solo va hasta ahora. No tengo experiencia con TamperData, así que no puedo hablar de eso.

La pregunta que debe hacerse es: "¿Cómo puedo verificar que las puntuaciones enviadas son válidas y auténticas?" La forma específica de hacerlo depende del juego. Para juegos de rompecabezas muy simples, puede enviar la puntuación junto con el estado de inicio específico y la secuencia de movimientos que dieron como resultado el estado final, y luego volver a ejecutar el juego en el lado del servidor utilizando los mismos movimientos. Confirme que la puntuación declarada es la misma que la calculada y solo acepte la puntuación si coinciden.

25
Stephen Deken

Una forma fácil de hacer esto sería proporcionar un hash criptográfico de su valor de puntuación más alta junto con la propia puntuación. Por ejemplo, al publicar los resultados a través de HTTP GET:http://example.com/highscores.php?score=500&checksum=0a16df3dc0301a36a34f9065c3ff8095

Al calcular esta suma de comprobación, se debe utilizar un secreto compartido; este secreto nunca debe ser transmitido a través de la red, sino que debe estar codificado dentro de la interfaz PHP y la interfaz de Flash. La suma de comprobación anterior se creó al anteponer la cadena " secret " a la puntuación " 500 ", y ejecutarla a través de md5sum.

Aunque este sistema evitará que un usuario publique puntuaciones arbitrarias, no evita un "ataque de repetición", donde un usuario vuelve a publicar una combinación de puntuación y hash calculada previamente. En el ejemplo anterior, una puntuación de 500 siempre producirá la misma cadena de hash. Parte de este riesgo se puede mitigar incorporando más información (como un nombre de usuario, marca de tiempo o dirección IP) en la cadena que se va a incluir. Aunque esto no evitará la reproducción de datos, asegurará que un conjunto de datos solo sea válido para un solo usuario a la vez.

Para evitar que ocurran any replay ataques, se deberá crear algún tipo de sistema de desafío-respuesta, como el siguiente:

  1. El juego flash ("el cliente") realiza un HTTP GET dehttp://example.com/highscores.phpsin parámetros. Esta página devuelve dos valores: un valor sal generado aleatoriamente, y un hash criptográfico de ese valor sal combinado con el secreto compartido. Este valor de sal debe almacenarse en una base de datos local de consultas pendientes y debe tener una marca de tiempo asociada para que pueda "caducar" después de un minuto.
  2. El juego flash combina el valor de sal con el secreto compartido y calcula un hash para verificar que coincida con el proporcionado por el servidor. Este paso es necesario para evitar la manipulación de los valores de sal por parte de los usuarios, ya que verifica que el servidor realmente generó el valor de sal.
  3. El juego flash combina el valor de sal con el secreto compartido, el alto valor de puntuación y cualquier otra información relevante (apodo, ip, marca de tiempo) y calcula un hash. Luego envía esta información al PHP backend a través de HTTP GET o POST, junto con el valor de sal, la puntuación más alta y otra información.
  4. El servidor combina la información recibida de la misma manera que en el cliente y calcula un hash para verificar que coincida con el proporcionado por el cliente. Luego, también verifica que el valor de sal todavía es válido como se muestra en la lista de consultas pendientes. Si ambas condiciones son verdaderas, escribe la puntuación más alta en la tabla de puntuaciones más altas y devuelve un mensaje de "éxito" firmado al cliente. También elimina el valor de sal de la lista de consultas pendientes.

Tenga en cuenta que la seguridad de cualquiera de las técnicas anteriores se ve comprometida si el secreto compartido es accesible para el usuario

Como alternativa, se podría evitar parte de este proceso de ida y vuelta forzando al cliente a comunicarse con el servidor a través de HTTPS, y asegurándose de que el cliente esté preconfigurado para confiar únicamente en los certificados firmados por una autoridad de certificación específica a la que solo usted tiene acceso. .

18
stormlash

Me gusta lo que dijo tpqf, pero en lugar de desactivar una cuenta cuando se descubre el engaño, implementa un honeypot para que cada vez que inicien sesión, vean sus puntuaciones pirateadas y nunca sospechen que han sido marcados como trolls. Busque en Google "phpBB MOD Troll" y verá un enfoque ingenioso.

11
DGM

Hice una especie de solución ... Me dieron un lugar donde las puntuaciones aumentaron (siempre se obtiene una puntuación de +1). Primero, comencé a contar a partir de números aleatorios (digamos 14) y cuando muestro las puntuaciones, solo muestro las puntuaciones var menos 14. Esto era así, si los crackers están buscando el ejemplo de 20, no lo encontrarán ( será 34 en la memoria). Segundo, ya que sé cuál debería ser el siguiente punto ... Utilicé la biblioteca crypto de Adobe para crear el hash de lo que el siguiente punto debería ser. Cuando tengo que incrementar los puntajes, verifico si el hash de los puntajes incrementados es igual a lo que debe ser el hash. Si el cracker ha cambiado los puntos en la memoria, los hashes no son iguales. Realizo algunas verificaciones del lado del servidor y cuando obtuve diferentes puntos del juego y del PHP, sé que las trampas estaban involucradas. Aquí está un fragmento de mi código (estoy usando la clase MD5 de Adobe Crypto libraty y sal de criptografía aleatoria. CallPhp () es mi validación del lado del 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 esta técnica + obfusticación de SWF, pude detener las galletas. Además, cuando envío las puntuaciones al servidor, uso mi propia función pequeña de cifrado/descifrado. Algo así (el código del lado del servidor no está incluido, pero puedes ver el algoritmo y escribirlo en 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
}

Luego envío la variable con otras falsas variables y simplemente se pierde entre la forma ... Es mucho trabajo solo para un pequeño juego flash, pero donde hay premios involucrados, algunas personas se vuelven codiciosas. Si necesitas ayuda, escríbeme un PM.

Saludos, Ico

En la respuesta aceptada, tqbf menciona que puedes hacer una búsqueda de memoria para la variable de puntuación ("Mi puntuación es 666, así que busco el número 666 en la memoria").

Hay una manera de evitar esto. Tengo una clase aquí: http://divillysausages.com/blog/safenumber_and_safeint

Básicamente, tienes un objeto para almacenar tu puntuación. En el establecedor, multiplica el valor que le pasas por un número aleatorio (+ y -), y en el captador, divides el valor guardado por el multiplicador aleatorio para recuperar el original. Es simple, pero ayuda a detener la búsqueda de memoria.

Además, vea el video de algunos de los tipos detrás del motor PushButton que hablan sobre algunas de las diferentes formas en que puede combatir el pirateo: http://zaa.tv/2010/12/the-art-of-hacking- juegos flash/ . Fueron la inspiración detrás de la clase.

4
divillysausages

No puede confiar en ningún dato que el cliente devuelva. La validación debe realizarse en el lado del servidor. No soy desarrollador de juegos, pero sí hago software empresarial. En ambos casos, el dinero puede estar involucrado y la gente romperá las técnicas de ofuscación del lado del cliente.

Tal vez envíe los datos al servidor periódicamente y haga alguna validación. No se centre en el código del cliente, incluso si ese es el lugar donde vive su aplicación.

3
zeller

Cifrar usando una clave reversible conocida (privada) sería el método más simple. No estoy tan entusiasmado con AS, así que no estoy seguro de qué tipo de proveedores de encriptación existen.

Pero podría incluir variables como la longitud del juego (cifrada, otra vez) y un conteo de clics.

Todas las cosas como esta pueden ser modificadas por ingeniería inversa, así que considera tirar un montón de datos basura para sacar a la gente del olor.

Edición: puede que valga la pena participar en algunas sesiones PHP también. Comience la sesión cuando haga clic en iniciar juego y (como indica el comentario de esta publicación) registre la hora. Cuando envían la puntuación, puedes comprobar que realmente tienen un juego abierto y no están enviando una puntuación demasiado pronto o demasiado grande.

Podría valer la pena elaborar un escalar para ver cuál es la puntuación máxima por segundo/minuto de juego.

Ninguna de estas cosas es incalculable, pero ayudará a tener cierta lógica no en Flash donde la gente pueda verla.

3
Oli

En mi experiencia, esto se aborda mejor como un problema de ingeniería social que como un problema de programación. En lugar de centrarse en hacer que sea imposible hacer trampa, concéntrese en hacerlo aburrido eliminando los incentivos para hacer trampa. Por ejemplo, si el incentivo principal son las puntuaciones altas visibles públicamente, simplemente poner un retraso en cuando se muestran las puntuaciones altas puede reducir significativamente el engaño al eliminar el circuito de retroalimentación positiva para los tramposos.

3
Scott Reynen

Usted está hablando de lo que se llama el problema de "confianza del cliente". Debido a que el cliente (en este efectivo, un SWF que se ejecuta en un navegador) está haciendo algo para lo que está diseñado. Guardar un puntaje alto.

El problema es que desea asegurarse de que las solicitudes de "puntuación de guardado" provengan de su película flash y no de una solicitud HTTP arbitraria. Una posible solución para esto es codificar un token generado por el servidor en el SWF en el momento de la solicitud (usando flasm ) que debe acompañar a la solicitud para guardar una puntuación alta. Una vez que el servidor guarda esa puntuación, el token caduca y ya no se puede utilizar para las solicitudes.

La desventaja de esto es que un usuario solo podrá enviar una puntuación alta por carga de la película flash; debe obligarlos a actualizar/recargar el SWF antes de poder volver a jugar para obtener una nueva puntuación.

2
Peter Bailey

La forma en que lo hace un nuevo y popular mod de arcade es que envía datos desde el flash a php, de nuevo a flash (o lo vuelve a cargar), y luego de nuevo a php. Esto le permite hacer cualquier cosa que desee para comparar los datos, así como evitar los trucos de descifrado o descifrado de datos posteriores y similares. Una forma de hacerlo es mediante la asignación de 2 valores aleatorios de php a la memoria flash (que no puede capturar o ver incluso si se está ejecutando un capturador de datos flash en tiempo real), usando una fórmula matemática para agregar la puntuación con los valores aleatorios y luego verificarla la misma fórmula para revertirla para ver si la puntuación coincide con la de cuando finalmente va al php al final. Estos valores aleatorios nunca son visibles, así como también se realiza la transacción y si es más de un par de segundos, también lo marca como trampa porque asume que ha detenido el envío para intentar averiguar los valores aleatorios o ejecutar los números a través de algún tipo de cifrado para devolver posibles valores aleatorios para comparar con el valor de la puntuación.

Esto parece una solución bastante buena si me preguntas, ¿alguien ve algún problema con el uso de este método? ¿O posibles maneras de evitarlo?

2
Vaughn

Creo que la forma más sencilla sería realizar llamadas a una función como RegisterScore (puntuación) cada vez que el juego registra una puntuación para agregarla y luego codificarla, empaquetarla y enviarla a un script php como una cadena. El script php sabría cómo decodificarlo correctamente. Esto detendría cualquier llamada directamente al script php, ya que cualquier intento de forzar una puntuación resultaría en un error de descompresión.

2
mathieu landry

Siempre que su sistema de puntuación alta se base en el hecho de que la aplicación Flash envía datos de puntuación alta sin firmar o sin firmar a través de la red, que pueden ser interceptados y manipulados/reproducidos. La respuesta se deriva de eso: cifrar (decentemente!) O firmar criptográficamente datos de puntuación más altos. Esto, al menos, hace que a las personas les resulte más difícil descifrar su sistema de puntuación más alta porque necesitarán extraer la clave secreta de su archivo SWF. Muchas personas probablemente se rendirán allí mismo. Por otro lado, todo lo que necesita es una persona individual para extraer la clave y publicarla en algún lugar.

Las soluciones reales implican una mayor comunicación entre la aplicación Flash y la base de datos de puntuación más alta para que esta última pueda verificar que una puntuación dada es algo realista. Esto probablemente sea complicado dependiendo del tipo de juego que tengas.

2
Jan Krüger

No hay forma de que sea completamente imposible de descifrar, ya que es fácil descompilar los SWF, y un pirata informático experto podría rastrear su código y descubrir cómo omitir cualquier sistema cifrado que pueda emplear.

Sin embargo, si simplemente desea evitar que los niños hagan trampas mediante el uso de herramientas simples como TamperData, podría generar una clave de cifrado que se pasa al SWF al inicio. Luego use algo como http://code.google.com/p/as3crypto/ para cifrar la puntuación más alta antes de volver a pasar al código PHP. Luego descifre en el extremo del servidor antes de almacenarlo en la base de datos.

2
David Arno

Normalmente incluyo los "datos fantasma" de la sesión del juego con la entrada de puntuación más alta. Así que si estoy haciendo un juego de carreras, incluyo los datos de repetición. A menudo ya tienes los datos de repetición para la funcionalidad de repetición o la funcionalidad de carreras de fantasmas (jugar contra tu última carrera o jugar contra el fantasma del tipo # 14 en la tabla de clasificación).

La verificación de estos es un trabajo muy manual, pero si el objetivo es verificar si las 10 mejores entradas en un concurso son legítimas, esto puede ser una adición útil al arsenal de medidas de seguridad que otros ya han mencionado.

Si el objetivo es mantener la lista de mejores puntuaciones en línea hasta el final de los tiempos sin que nadie tenga que mirarlas, esto no le traerá mucho.

2
Lucas Meijer

Solo es posible manteniendo toda la lógica del juego en el lado del servidor que también almacena la puntuación internamente sin el conocimiento del usuario. Por razones económicas y científicas, la humanidad no puede aplicar esta teoría a todos los tipos de juegos, excepto los basados ​​en turnos. Por ejemplo mantener la física en el lado del servidor es computacionalmente costoso y difícil de responder como la velocidad de la mano. Incluso es posible, mientras juegas al ajedrez, cualquiera puede igualar el juego de ajedrez AI con el oponente. Por lo tanto, better multijugador también debe contener creatividad bajo demanda.

2
placeohlder

No es realmente posible lograr lo que quieres. Las partes internas de la aplicación Flash son siempre parcialmente accesibles, especialmente cuando sabes cómo usar CheatEngine , lo que significa que no importa qué tan seguras sean las comunicaciones de tu sitio web y del navegador <->, todavía será relativamente simple para superar.

1
Mark Webster

Podría ser una buena idea comunicarse con el backend a través de AMFPHP . Debería desalentar al menos a los perezosos a intentar empujar los resultados a través de la consola del navegador.

1
m1gu3l