web-dev-qa-db-fra.com

Comment aborder le remplacement de md5 pour le transport des données de jeu Unity vers un serveur distant

TL; DR

Je travaille sur un système de jeu qui utilise nityScript et C # sur le client et PHP sur le serveur. Un hachage MD5 des données plus un secret partagé est utilisé pour vérifiez que les données n'ont pas été modifiées en transit. Le MD5 est-il assez bon pour cela? Quel autre algorithme de hachage puis-je utiliser qui fonctionne dans les trois langues?

Le problème plus en détail

J'ai rencontré du code sur un site Web communautaire largement utilisé sur la populaire plate-forme de développement de jeux nity , et je travaille actuellement sur l'amélioration de MySQL, PHP et la sécurité de ce code.

Le code utilise une valeur de "clé secrète" partagée entre le client et le serveur. Tous les messages du client incluent un hachage des données (par exemple nom et score) plus la clé secrète, que le serveur vérifie avant d'accepter les données. Il s'agit essentiellement d'une authentification dont les données transmises n'ont pas été falsifiées.

Cependant, parce que c'est MD5, je pense que quelqu'un qui écoute le trafic réseau pourrait facilement trouver la clé secrète et ensuite publier toutes les données qu'il souhaite sur le serveur.

Mes questions sont donc:

  • Cette situation actuelle mérite-t-elle une amélioration? Ou s'agit-il de l'utilisation actuelle prévue de MD5 (en janvier 2017)?
  • Existe-t-il un autre algorithme de hachage qui pourrait améliorer/authentifier davantage cette activité de communication? Veuillez noter que l'algorithme devrait fonctionner en PHP, UnityScript et C #.

Le code

  • Dans UnityScript (côté client):

    var hash=Md5.Md5Sum(name + score + secretKey);
    
  • En C # (côté client):

    string hash = MD5Test.Md5Sum(name + score + secretKey);
    
  • Dans PHP (côté serveur):

    $secretKey = "mySecretKey"; // Change this value to match the value 
                                //stored in the client javascript below 
    
    $realHash = md5($_GET['name'] . $_GET['score'] . $secretKey); 
    if($realHash == $hash) {
           //interact with database 
    }
    

Autres critères

  • Certains programmeurs Unity utilisent UnityScript (un langage de type Python avec une syntaxe de type JavaScript sur l'API .NET) mais aucune bibliothèque ne peut être supposée installée.

  • Les développeurs de jeux sont pas PHP/MySQL si complexes ou des codes PHP/C #/Js tiers ou tiers ne seraient probablement pas utiles.

  • BCrypt est apparemment imprudent à utiliser avec C #

  • BCrypt a besoin d'un sel statique dans ce cas (peut-être dérivé de la clé secrète?) Mais est destiné à fonctionner avec un sel aléatoire.

  • PBKDF2 semble être préféré à BCrypt mais peut être très lent en particulier sur les appareils mobiles sans beaucoup de mémoire.

  • On ne peut pas s'attendre à traiter avec un serveur sécurisé (si seulement ...).

  • Je ne connais pas suffisamment la bibliothèque de sécurité C # pour vraiment choisir les meilleures options parmi celles répertoriées.

  • Bien que le code décrit ne concerne que les mises à jour de haut niveau, ce code a été utilisé dans le passé - et sera à l'avenir - utilisé et utilisé pour transporter toutes sortes de données, publiques et privées, vers diverses bases de données.

  • Gérer l'interopérabilité des algorithmes de hachage entre PHP, UnityScript et C # est un obstacle plus important que je ne l'avais prévu. Si c'était juste PHP j'utiliserais password_hash.

Quelques réflexions et historique de cette question:

J'ai mis à jour le titre car le titre édité semblait suggérer que je n'étais pas sûr de changer MD5, alors que savoir que je devais changer MD5 était l'une des principales raisons de poser la question ici en premier lieu.

La question initiale était que je voulais mettre à jour les terribles suggestions de code données (entre autres) ici sur la façon de gérer les interactions entre un jeu sur une machine cliente et le stockage de données sur un serveur distant. Gardez à l'esprit qu'il s'agit de suggestions de code pour les programmeurs débutants dans Unity et que ce site est [maintenant] géré par Unity Technologies elles-mêmes.
Si vous regardez, la question d'origine (liée ci-dessus) utilisait PHP Mysql_ fonctions (ainsi qu'une forme de PDO invalide plutôt merdique). Je pensais que cela bénéficierait d'une réécriture. J'ai vu que le code d'origine avait également utilisé une routine md5 pour hacher les données prévues. En ce qui concerne le remplacement de MD5, je n'avais réalisé ni la vulnérabilité des fichiers de projet compilés ni la taille/échelle du travail nécessaire pour rendre ce bloc de code plus sûr (sur l'interaction avec le serveur ou le côté client). Les données). Ma quête initiale était de trouver une solution de remplacement appropriée pour le MD5 qui pourrait fonctionner dans les différents langages requis (UnityScript, C #, PHP), car j'étais conscient de ses lacunes. Je n'avais pas réalisé (à en juger par les commentaires ici) à quel point il est vraiment facile de pénétrer dans les exes et de saisir des données codées en dur.

Cette question ne concerne [~ # ~] pas [~ # ~] sur un jeu que je fais, c'est pas à propos de mon projet et le code cité que j'ai l'intention de remplacer n'a pas été écrit par moi . J'ai lu beaucoup de commentaires selon lesquels les gens essayent de quelque façon le messager, mais cette question est venue de mon propre souhait d'améliorer une lacune existante sur un site Web wiki d'enseignement. J'ai le plus grand respect pour les connaissances partagées en répondant à cette question, mais je suis conscient que depuis les 6 derniers mois d'exploration de la documentation et des sites d'apprentissage Unity, il y a une lacune importante dans la sécurisation des applications locales et du multijoueur ou d'autres interactions à distance.

Je vois beaucoup de répondants dans les commentaires affirmant que la réponse donnée par George est une mauvaise réponse - mais elle répond à la question spécifique que j'ai posée à l'époque. Merci.

Note finale:
Cet article Blob d'après les commentaires de Luke Briggs a souligné à quel point il est facile de manipuler les données locales de l'application Unity Game Application. Je n'ai pas du tout compris comment les fichiers locaux vulnérables sont ....

49
Martin

Edit: Pour clarifier, je ne peux pas parler de la sécurité de votre approche, seulement la partie de hachage. Il y a ici de forts commentaires décourageant votre approche. (C'est-à-dire qu'un bon hachage peut faire partie d'une approche sécurisée ou d'une approche non sécurisée, tout comme un bon verrou peut être utilisé sur une bonne porte/cadre ou une porte/cadre fragile)

En réponse à votre TL; DR, je peux affirmer que SHA-2 est la fonction de hachage de remplacement recommandée.

Si la longueur est un problème, il est préférable d'utiliser un SHA-2 tronqué que le MD5.

SHA-2 est disponible en 4 tailles: 224, 256, 384 ou 512; 256 et 512 étant les plus courants.

Gardez à l'esprit qu'il s'agit d'un hachage à usage général (rapide) et ne convient pas pour le stockage de mots de passe, mais il convient pour garantir l'intégrité des données comme le suggère votre TL; DR.

BCrypt est un hachage de mot de passe lent et est utilisé si vous avez besoin d'une sorte de cryptage à sens unique. (c'est-à-dire que le mot de passe n'est jamais stocké, seulement son hachage, et est difficile à forcer brutalement)

27
Bryan Field

Cette approche est fondamentalement viciée. Tout ce qui concerne le côté client peut être et sera altéré par les joueurs. C'est le même problème qui rend le DRM intenable - l'utilisateur possède la machine et toutes les données qui s'y trouvent, y compris vos exécutables, les données en mémoire, etc. Garder les algorithmes secrets ne fonctionne pas (voir principe de Kerckhoffs ), car il suffit d'un peu d'ingénierie inverse pour déterminer ce que fait votre code.

Par exemple, disons que votre client de jeu a une routine qui affiche le score de niveau sur le serveur, en utilisant une cryptographie de quelque forme que ce soit pour s'assurer qu'elle n'est pas falsifiée sur le réseau. Il existe de nombreuses façons de contourner ce problème:

  • Utilisez un outil d'édition de mémoire tel que Cheat Engine pour rechercher le score actuel (pause, rechercher le score, annuler la pause, attendez que le score change, recherchez à nouveau, répétez jusqu'à ce que vous trouviez l'adresse mémoire qui contient la valeur) et modifiez-la. Une fois le niveau terminé, la valeur du score sera traitée avec plaisir comme légitime par votre code et téléchargée sur le serveur.
  • Modifiez l'exécutable du jeu sur le disque afin que votre code "niveau complet" ignore la valeur réelle du score et en sélectionne une autre.
  • Modifiez l'exécutable du jeu sur le disque afin que des choses simples (par exemple, tuer un monstre) augmentent votre score de 1000 fois plus qu'il ne devrait le faire.
  • Modifiez l'exécutable du jeu sur le disque de sorte que vous ne puissiez jamais mourir, ou que vous ayez des bonus infinis, ou des tirs en un coup, ou tout autre nombre d'aide, afin que vous puissiez facilement atteindre un score très élevé.
  • Effectuez l'une de ces modifications en mémoire après le chargement du jeu afin que l'exécutable d'origine reste intact (utile dans les cas où il y a des vérifications d'intégrité ennuyeuses)
  • Exposez simplement le code "nous avons terminé un niveau, téléchargez maintenant le score" en externe du processus afin qu'il puisse être appelé par n'importe quel programme. En code natif, vous pouvez injecter un petit talon et ajouter une entrée à la table d'exportation, ou simplement copier directement le code dans votre propre exécutable. Dans .NET, il est trivial de simplement modifier les indicateurs de visibilité de la classe et de la méthode et de l'importer dans un nouveau programme. Vous pouvez maintenant soumettre les valeurs de score que vous souhaitez sans même lancer le jeu.
  • Effectuer une rétro-ingénierie du jeu et saisir la clé "secrète" et écrire votre propre application pour envoyer la valeur du score au serveur.

Ce n'est que la pointe de l'iceberg. Pour les jeux plus complexes, il existe toutes sortes de solutions de contournement à l'anti-triche et à d'autres problèmes, mais quelles que soient les astuces de défense utilisées, il y aura toujours un moyen de jouer avec les valeurs côté client.

La caractéristique critique d'une approche sécurisée est que rien sur l'ordinateur du joueur ne doit être approuvé . Lorsque votre code de serveur reçoit un paquet d'un joueur, supposez que votre jeu ne soit même pas en cours d'exécution - il pourrait s'agir d'un morceau de code totalement homebrew qui repose sur tout.

Sécuriser les jeux multijoueurs (ou tout type de jeu où la vérification de l'état du jeu est une exigence) n'est pas facile. Le problème n'est même pas vraiment un problème de sécurité, c'est un problème d'utilisation et de performance. Le moyen le plus simple de sécuriser un jeu multijoueur est de conserver tout l'état du jeu côté serveur, de faire exécuter et de maintenir toute la logique du jeu, et de faire en sorte que le client ne fasse rien d'autre que d'envoyer les données du joueur au serveur ("l'utilisateur a cliqué sur la souris , l'utilisateur tient la touche W ") et présente le son et la vidéo au lecteur. Le problème avec cela est que cela ne fait pas un jeu très amusant en raison de la latence du réseau, et il est assez difficile à mettre à l'échelle côté serveur. Au lieu de cela, vous devez trouver un équilibre entre garder les choses côté client et côté serveur. Par exemple, la logique pour "le joueur a-t-il une clé pour ouvrir cette porte?" doit être cochée côté serveur, mais la logique de l'affichage de l'icône de contexte pour "porte ouverte" reste côté client. Encore une fois, des choses comme la santé et la position de l'ennemi doivent être conservées côté serveur, et la capacité du joueur à infliger des dégâts à cet ennemi doit également être vérifiée (est-elle suffisamment proche?), Ce qui signifie que l'IA ne peut pas être conservée côté client, mais des choses comme le choix de l'animation inactive que l'ennemi affiche seront probablement du côté client.

Nous avons quelques questions ici sur la sécurité du jeu, je vous suggère donc de les lire:

Je recommande également ces questions sur GameDev SE:

Il y a aussi n excellent article sur la sécurité multijoueur de BlackHat EU 2013, et un article sur les réseaux P2P sans autorité , qui peuvent tous deux être utiles.

202
Polynomial

Il y a trois problèmes ici:

  • Ce que vous essayez de faire est fondamentalement erroné de plusieurs manières, comme décrit dans la réponse de Polynomial.

  • Si vous insistez pour le faire de toute façon, alors vous n'utilisez même pas la bonne primitive! L'authentification des messages nécessite un MAC , pas un hachage. Il est possible de construire un MAC à partir d'un hachage - c'est ce que fait HMAC - mais ce n'est pas aussi trivial qu'il n'y paraît. Ce que le code que vous avez collé, c'est d'essayer de construire naïvement un MAC à partir d'un hachage, et ces types de constructions naïves sont généralement brisées.

  • Ouais, MD5 est une mauvaise fonction de hachage. Mais c'est vraiment le moindre de vos soucis ... MD5 n'est pas le point faible de cette conception. Vous demandez des conseils sur la façon de remplacer les barres au-dessus de vos fenêtres par un placage d'acier de 2 pouces d'épaisseur - mais votre porte d'entrée est grande ouverte. Justifier cela comme étant une "meilleure sécurité" n'a aucun sens.

40
Nathaniel J. Smith

Un de mes amis a déjà fait un jeu flash (oui, il y a si longtemps) et a utilisé le même schéma: md5(authenticated data + secret key), puis le valider sur le serveur.

Je l'ai inversé avec un outil et j'ai trouvé la valeur secrète qu'il utilisait en environ 30 minutes. Jeu terminé.

Mais oui, c'est la deuxième meilleure option possible. Indépendamment de l'utilisation de sha2 au lieu de md5 et indépendamment de l'utilisation de hmac au lieu d'un algorithme de hachage, l'entrée utilisateur n'est jamais fiable. Si les utilisateurs exécutent votre jeu sur leur machine (c'est-à-dire qu'il est sous leur contrôle) et s'ils peuvent soumettre un score élevé, ils pourront toujours utiliser des outils pour améliorer les scores.

La meilleure chose que vous puissiez faire est de télécharger une relecture (par exemple, des touches enregistrées) et de la réexécuter sur le serveur, puis de valider l'heure résultante. C'est beaucoup de travail à coder, prend plus de ressources de serveur, et vous aurez besoin d'avoir une physique déterministe si vous utilisez des flotteurs n'importe où ... mais c'est la seule chose meilleure que ce que vous faites, et les gens peuvent toujours écrire le script jeu s'ils veulent tricher.

11
Luc