Je parle d'un jeu d'action sans limite de score supérieure et sans moyen de vérifier le score sur le serveur en rejouant des mouvements, etc.
Ce dont j'ai vraiment besoin, c'est du cryptage le plus puissant possible dans Flash/PHP, et d'un moyen d'empêcher les gens d'appeler la page PHP autrement que via mon fichier Flash. J'ai essayé quelques méthodes simples dans le passé de effectuer plusieurs appels pour un seul score et terminer une séquence de somme de contrôle/fibonacci, etc., et également obscurcir le SWF avec Amayeta SWF Encrypt, mais ils ont tous été piratés finalement.
Grâce aux réponses de StackOverflow, j'ai maintenant trouvé plus d'informations sur Adobe - http://www.Adobe.com/devnet/flashplayer/articles/secure_swf_apps_12.html et https: // github .com/mikechambers/as3corelib - que je pense pouvoir utiliser pour le cryptage. Je ne suis pas sûr que cela me permette de contourner CheatEngine.
J'ai besoin de connaître les meilleures solutions pour AS2 et AS3, si elles sont différentes.
Les principaux problèmes semblent être des choses comme les en-têtes TamperData et LiveHTTP, mais je comprends qu'il existe également des outils de piratage plus avancés - comme CheatEngine (merci Mark Webster)
Il s'agit d'un problème classique avec les jeux et concours sur Internet. Votre code Flash fonctionne avec les utilisateurs pour décider du score d'un jeu. Mais les utilisateurs ne sont pas fiables et le code Flash s'exécute sur l'ordinateur de l'utilisateur. Vous êtes SOL. Il n'y a rien que vous puissiez faire pour empêcher un attaquant de se forger des scores élevés:
Flash est encore plus facile à rétroconcevoir que vous ne le pensez, car les bytecodes sont bien documentés et décrivent un langage de haut niveau (Actionscript) --- lorsque vous publiez un jeu Flash, vous publiez votre code source, que vous le sais ou pas.
Les attaquants contrôlent la mémoire d'exécution de l'interpréteur Flash, afin que quiconque sait utiliser un débogueur programmable puisse à tout moment modifier n'importe quelle variable (y compris le score actuel) ou modifier le programme lui-même.
L'attaque la plus simple possible contre votre système consiste à exécuter le trafic HTTP pour le jeu via un proxy, à capturer la sauvegarde du meilleur score et à la rejouer avec un score plus élevé.
Vous pouvez essayer de bloquer cette attaque en liant chaque enregistrement de score élevé à une seule instance du jeu, par exemple en envoyant un jeton chiffré au client au démarrage du jeu, qui pourrait ressembler à:
hex-encoding( AES(secret-key-stored-only-on-server, timestamp, user-id, random-number))
(Vous pouvez également utiliser un cookie de session dans le même sens).
Le code du jeu renvoie ce jeton au serveur avec la sauvegarde du meilleur score. Mais un attaquant peut tout simplement relancer le jeu, obtenir un jeton, puis coller immédiatement ce jeton dans une sauvegarde de score élevé rejouée.
Ainsi, vous alimentez non seulement un jeton ou un cookie de session, mais également une clé de session de cryptage à score élevé. Ce sera une clé AES 128 bits, elle-même chiffrée avec une clé codée en dur dans le jeu Flash:
hex-encoding( AES(key-hardcoded-in-flash-game, random-128-bit-key))
Maintenant, avant que le jeu ne publie le meilleur score, il déchiffre la clé de session de cryptage de score élevé, ce qu'il peut faire parce que vous avez codé en dur la clé de décryptage de clé de session de cryptage élevé dans le binaire Flash. Vous cryptez le score élevé avec cette clé décryptée, ainsi que le hachage SHA1 du score élevé:
hex-encoding( AES(random-128-bit-key-from-above, high-score, SHA1(high-score)))
Le code PHP sur le serveur vérifie le jeton pour s'assurer que la demande provient d'une instance de jeu valide, puis déchiffre le score élevé crypté, vérifiant que le score élevé correspond au SHA1 du score élevé (si vous sautez cette étape, le décryptage produira simplement des scores aléatoires, probablement très élevés et élevés).
Alors maintenant, l'attaquant décompile votre code Flash et trouve rapidement le code AES, qui ressort comme un pouce endolori, même si ce n'était pas le cas, il serait retrouvé en 15 minutes avec une recherche de mémoire et un traceur ("Je sais mon score pour ce jeu est 666, alors trouvons 666 en mémoire, puis attrapons toute opération qui touche cette valeur --- oh regardez, le code de cryptage de score élevé! "). Avec la clé de session, l'attaquant n'a même pas à exécuter le code Flash; elle attrape un jeton de lancement de jeu et une clé de session et peut renvoyer un score élevé arbitraire.
Vous êtes maintenant au point où la plupart des développeurs abandonnent simplement --- donnent ou prennent quelques mois à jouer avec les attaquants en:
Brouillage des clés AES avec les opérations XOR
Remplacement des tableaux d'octets clés par des fonctions qui calculent la clé
Diffusion de faux chiffrements clés et publications de scores élevés dans tout le binaire.
C'est surtout une perte de temps. Il va sans dire que SSL ne vous aidera pas non plus; SSL ne peut pas vous protéger lorsque l'un des deux points de terminaison SSL est mauvais.
Voici quelques éléments qui peuvent réellement réduire la fraude aux scores élevés:
Exiger une connexion pour jouer au jeu, que la connexion produise un cookie de session et n'autorise pas plusieurs lancements de jeu en suspens sur la même session ou plusieurs sessions simultanées pour le même utilisateur.
Rejetez les scores élevés des sessions de jeu qui durent moins que les vrais jeux les plus courts jamais joués (pour une approche plus sophistiquée, essayez de "mettre en quarantaine" les scores élevés pour les sessions de jeu qui durent moins de 2 écarts-types en dessous de la durée moyenne du jeu). Assurez-vous de suivre la durée du jeu côté serveur.
Rejetez ou mettez en quarantaine les scores élevés des connexions qui n'ont joué qu'une ou deux fois au jeu, de sorte que les attaquants doivent produire une "trace papier" de jeu à la recherche raisonnable pour chaque connexion qu'ils créent.
"Heartbeat" marque pendant le jeu, de sorte que votre serveur voit la croissance du score sur la durée de vie d'un jeu. Rejetez les scores élevés qui ne suivent pas des courbes de score raisonnables (par exemple, en passant de 0 à 999999).
État du jeu "Instantané" pendant le jeu (par exemple, quantité de munitions, position dans le niveau, etc.), que vous pourrez ensuite rapprocher des scores intermédiaires enregistrés. Vous n'avez même pas besoin d'avoir un moyen de détecter les anomalies dans ces données pour commencer; il vous suffit de le collecter, puis vous pouvez revenir en arrière et l'analyser si les choses semblent louche.
Désactivez le compte de tout utilisateur qui échoue à l'un de vos contrôles de sécurité (par exemple, en soumettant un score élevé chiffré qui échoue à la validation).
Rappelez-vous cependant que vous ne faites que dissuader la fraude des meilleurs scores ici. Il y a rien que vous pouvez faire pour éviter si. S'il y a de l'argent en jeu dans votre jeu, quelqu'un va vaincre n'importe quel système que vous inventez. L'objectif n'est pas de stop cette attaque; c'est pour rendre l'attaque plus chère que de devenir vraiment bon dans le jeu et de le battre.
Vous posez peut-être la mauvaise question. Vous semblez concentré sur les méthodes que les gens utilisent pour progresser dans la liste des meilleurs scores, mais bloquer des méthodes spécifiques ne va que jusqu'à présent. Je n'ai aucune expérience avec TamperData, donc je ne peux pas en parler.
La question que vous devriez vous poser est: "Comment puis-je vérifier que les scores soumis sont valides et authentiques?" La façon spécifique de le faire dépend du jeu. Pour les jeux de puzzle très simples, vous pouvez envoyer le score avec l'état de départ spécifique et la séquence de mouvements qui ont abouti à l'état final, puis relancer le jeu côté serveur en utilisant les mêmes mouvements. Confirmez que le score indiqué est le même que le score calculé et n'acceptez le score que s'il correspond.
Un moyen facile de le faire serait de fournir un hachage cryptographique de votre valeur de score élevé avec le score lui-même. Par exemple, lors de la publication des résultats via HTTP GET: http://example.com/highscores.php?score=500&checksum=0a16df3dc0301a36a34f9065c3ff8095
Lors du calcul de cette somme de contrôle, un secret partagé doit être utilisé; ce secret ne doit jamais être transmis sur le réseau, mais doit être codé en dur dans le backend PHP et le frontend flash. La somme de contrôle ci-dessus a été créée en ajoutant la chaîne " secret "au score" 500 ", et en le passant par md5sum.
Bien que ce système empêche un utilisateur de publier des scores arbitraires, il n'empêche pas une "attaque de relecture", où un utilisateur repositionne un score et une combinaison de hachage précédemment calculés. Dans l'exemple ci-dessus, un score de 500 produirait toujours la même chaîne de hachage. Certains de ces risques peuvent être atténués en incorporant plus d'informations (comme un nom d'utilisateur, un horodatage ou une adresse IP) dans la chaîne qui doit être hachée. Bien que cela n'empêchera pas la relecture des données, cela garantira qu'un ensemble de données n'est valide que pour un seul utilisateur à la fois.
Pour empêcher toute attaque de relecture , un certain type de système de défi-réponse devra être créé, tel que le suivant:
Veuillez garder à l'esprit que la sécurité de l'une des techniques ci-dessus est compromise si le secret partagé est jamais accessible à l'utilisateur
Comme alternative, certains de ces allers-retours pourraient être évités en forçant le client à communiquer avec le serveur via HTTPS et en s'assurant que le client est préconfiguré pour ne faire confiance qu'aux certificats signés par une autorité de certification spécifique à laquelle vous seul avez accès. .
J'aime ce que tpqf a dit, mais plutôt que de désactiver un compte lorsque la tricherie est découverte, implémentez un pot de miel afin que chaque fois qu'ils se connectent, ils voient leurs scores piratés et ne soupçonnent jamais qu'ils ont été marqués comme troll. Google pour "phpBB MOD Troll" et vous verrez une approche ingénieuse.
J'ai fait une sorte de solution de contournement ... J'ai eu un donné où les scores ont augmenté (vous obtenez toujours un score de +1). Tout d'abord, j'ai commencé à compter à partir d'un nombre aléatoire (disons 14) et lorsque j'affiche les scores, je viens de montrer les scores var moins 14. C'était le cas si les crackers recherchent par exemple 20, ils ne le trouveront pas (il sera 34 en mémoire). Deuxièmement, puisque je sais quel devrait être le prochain point ... J'ai utilisé la bibliothèque de cryptographie Adobe, pour créer le hachage de ce que le prochain point devrait être. Lorsque je dois incrémenter les scores, je vérifie si le hachage des scores incrémentés est égal au hachage qu'il devrait être. Si le cracker a changé les points dans la mémoire, les hachages ne sont pas égaux. J'effectue une vérification côté serveur et quand j'ai obtenu différents points du jeu et du PHP, je sais que la triche était impliquée. Voici un extrait de mon code (j'utilise la classe Adobe Crypto libraty MD5 et le sel de cryptographie aléatoire. CallPhp () est ma validation côté serveur)
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");
}
}
En utilisant cette technique + obfustication SWF, j'ai pu arrêter les crackers. De plus, lorsque j'envoie les partitions côté serveur, j'utilise ma propre petite fonction de cryptage/décryptage. Quelque chose comme ça (code côté serveur non inclus, mais vous pouvez voir l'algorithme et l'écrire 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
}
Ensuite, j'envoie la variable avec d'autres faux-vars et elle se perd juste en cours de route ... C'est beaucoup de travail pour un petit jeu flash, mais lorsque des prix sont impliqués, certaines personnes deviennent simplement gourmandes. Si vous avez besoin d'aide, écrivez-moi un PM.
À la vôtre, Ico
Dans la réponse acceptée, tqbf mentionne que vous pouvez simplement faire une recherche en mémoire pour la variable de score ("Mon score est 666 donc je cherche le nombre 666 en mémoire").
Il y a un moyen de contourner cela. J'ai une classe ici: http://divillysausages.com/blog/safenumber_and_safeint
Fondamentalement, vous avez un objet pour stocker votre score. Dans le setter, il multiplie la valeur que vous lui transmettez par un nombre aléatoire (+ et -), et dans le getter, vous divisez la valeur enregistrée par le multiplicateur aléatoire pour récupérer l'original. C'est simple, mais aide à arrêter la recherche de mémoire.
Consultez également la vidéo de certains des gars derrière le moteur PushButton qui parlent de différentes façons de lutter contre le piratage: http://zaa.tv/2010/12/the-art-of- hacking-flash-games / . Ils étaient l'inspiration derrière la classe.
Vous ne pouvez pas faire confiance aux données retournées par le client. La validation doit être effectuée côté serveur. Je ne suis pas développeur de jeux, mais je crée des logiciels d'entreprise. Dans les deux cas, de l'argent peut être impliqué et les gens vont casser les techniques d'obscurcissement côté client.
Peut-être renvoyer périodiquement des données au serveur et effectuer une validation. Ne vous concentrez pas sur le code client, même si c'est là que réside votre application.
Le chiffrement à l'aide d'une clé réversible (privée) connue serait la méthode la plus simple. Je ne suis pas tout à fait sur AS, donc je ne sais pas quels types de fournisseurs de chiffrement il y a.
Mais vous pouvez inclure des variables comme la durée du jeu (cryptée, encore) et le nombre de clics.
Toutes ces choses peut être rétro-conçues, alors pensez à jeter un tas de données indésirables pour jeter les gens hors du parfum.
Edit: Cela peut valoir la peine de rentrer dans certaines PHP sessions aussi. Démarrez la session quand ils cliquent sur démarrer le jeu et (comme le dit le commentaire de cet article) enregistrez l'heure. Quand ils soumettent le score, vous peut vérifier qu'ils ont effectivement un jeu ouvert et qu'ils ne soumettent pas de score trop tôt ou trop grand.
Il pourrait être utile d'élaborer un scalaire pour voir quel est le score maximum par seconde/minute de jeu.
Aucune de ces choses n'est incontournable, mais cela aidera à avoir une logique qui ne se trouve pas dans le Flash où les gens peuvent la voir.
D'après mon expérience, il est préférable d'aborder cela comme un problème d'ingénierie sociale plutôt que comme un problème de programmation. Plutôt que de se concentrer sur l'impossibilité de tricher, concentrez-vous sur le rendre ennuyeux en supprimant les incitations à tricher. Par exemple, si la principale incitation est des scores élevés visibles par le public, le simple fait de retarder l'affichage des scores élevés peut réduire considérablement la tricherie en supprimant la boucle de rétroaction positive pour les tricheurs.
Vous parlez de ce qu'on appelle le problème de la "confiance du client". Parce que le client (dans cet argent, un SWF fonctionnant dans un navigateur) fait quelque chose pour lequel il est conçu. Enregistrez un score élevé.
Le problème est que vous voulez vous assurer que les demandes de "sauvegarde de score" proviennent de votre film flash, et non d'une demande HTTP arbitraire. Une solution possible pour cela consiste à coder un jeton généré par le serveur dans le SWF au moment de la demande (en utilisant flasm ) qui doit accompagner la demande pour enregistrer un score élevé. Une fois que le serveur a enregistré ce score, le jeton a expiré et ne peut plus être utilisé pour les demandes.
L'inconvénient est qu'un utilisateur ne pourra soumettre qu'un seul score élevé par chargement du film flash - vous devez les forcer à actualiser/recharger le SWF avant de pouvoir jouer à nouveau pour un nouveau score.
La façon dont un nouveau mod d'arcade populaire le fait est qu'il envoie des données du flash au php, de nouveau au flash (ou le recharge), puis de nouveau au php. Cela vous permet de faire tout ce que vous voulez pour comparer les données ainsi que de contourner les hacks post-données/décryptage et autres. Une façon de le faire est d'assigner 2 valeurs aléatoires de php dans le flash (que vous ne pouvez pas saisir ou voir même si vous exécutez un récupérateur de données flash en temps réel), en utilisant une formule mathématique pour ajouter le score avec les valeurs aléatoires, puis en le vérifiant en utilisant la même formule pour l'inverser pour voir si le score correspond quand il va finalement au php à la fin. Ces valeurs aléatoires ne sont jamais visibles, car elles chronomètrent également la transaction en cours et si elles durent plus de quelques secondes, elles le signalent également comme tricheur, car elles supposent que vous avez arrêté l'envoi pour essayer de comprendre les valeurs aléatoires ou exécuter les nombres à travers un certain type de chiffrement pour retourner des valeurs aléatoires possibles à comparer avec la valeur du score.
Cela semble être une assez bonne solution si vous me demandez, est-ce que quelqu'un voit des problèmes avec cette méthode? Ou des solutions possibles?
Je pense que le moyen le plus simple serait d'appeler une fonction comme RegisterScore (score) chaque fois que le jeu enregistre un score à ajouter, puis de le coder, de l'empaqueter et de l'envoyer à un script php sous forme de chaîne. Le script php saurait le décoder correctement. Cela arrêterait tout appel directement au script php car toute tentative de forcer un score entraînerait une erreur de décompression.
Chaque fois que votre système de score élevé est basé sur le fait que l'application Flash envoie des données de score élevé non cryptées/non signées via le réseau, qui peuvent être interceptées et manipulées/rejouées. La réponse découle de cela: crypter (décemment!) Ou signer cryptographiquement des données à haut score. Cela, au moins, rend plus difficile pour les gens de casser votre système de meilleurs scores car ils devront extraire la clé secrète de votre fichier SWF. Beaucoup de gens abandonneront probablement juste là. D'un autre côté, il suffit d'une seule personne pour extraire la clé et la poster quelque part.
Les vraies solutions impliquent davantage de communication entre l'application Flash et la base de données à haut score afin que cette dernière puisse vérifier qu'un score donné est quelque peu réaliste. C'est probablement compliqué selon le type de jeu que vous avez.
Il n'y a aucun moyen de le rendre complètement inébranlable, car il est facile de décompiler les fichiers SWF, et un développeur pirate qualifié pourrait alors suivre votre code et trouver comment contourner tout système crypté que vous pourriez utiliser.
Si vous voulez simplement empêcher les enfants de tricher via des outils simples comme TamperData, vous pouvez générer une clé de chiffrement que vous transmettez au SWF au démarrage. Utilisez ensuite quelque chose comme http://code.google.com/p/as3crypto/ pour chiffrer le meilleur score avant de le renvoyer au code PHP. Ensuite, déchiffrez à la fin du serveur avant de le stocker dans la base de données.
J'inclus généralement les "données fantômes" de la session de jeu avec l'entrée de meilleur score. Donc, si je fais un jeu de course, j'inclus les données de rejeu. Vous avez souvent déjà les données de relecture pour la fonctionnalité de relecture ou la fonctionnalité de course fantôme (jouer contre votre dernière course, ou jouer contre le fantôme du mec # 14 sur le classement).
Les vérifier est un travail très manuel, mais si l'objectif est de vérifier si les 10 meilleures inscriptions à un concours sont légitimes, cela peut être un ajout utile à l'arsenal de mesures de sécurité que d'autres ont déjà souligné.
Si l'objectif est de garder la liste des meilleurs scores en ligne jusqu'à la fin des temps sans que personne n'ait à les regarder, cela ne vous apportera pas grand-chose.
Cela n'est possible qu'en gardant le toute la logique du jeu côté serveur qui stocke également le score en interne à l'insu de l'utilisateur. Pour des raisons économiques et scientifiques, l'humanité ne peut pas appliquer cette théorie à tous les types de jeu, sauf au tour par tour. Par exemple garder la physique côté serveur est coûteux en calcul et difficile à obtenir en réponse à la vitesse de la main. Même possible, tout en jouant aux échecs, tout le monde peut faire correspondre le jeu d'échecs de l'IA à l'adversaire. Par conséquent, mieux jeux multijoueurs devrait également contenir de la créativité à la demande.
Ce n'est pas vraiment possible de réaliser ce que vous voulez. Les composants internes de l'application Flash sont toujours partiellement accessibles, en particulier lorsque vous savez comment utiliser des choses comme CheatEngine , ce qui signifie que peu importe la sécurité de votre site Web et de votre navigateur <-> les communications du serveur sont, cela va toujours être relativement simple à surmonter.
Ce pourrait être une bonne idée de communiquer avec le backend via AMFPHP . Cela devrait décourager au moins les paresseux d'essayer de pousser les résultats via la console du navigateur.