Jump to content
Unity Insider Forum
Schalla

[HowTo] PHP Keygenerator inkl. Abfrage in Unity

Recommended Posts

Hallo zusammen,

 

ich bin persönlich ein Neuling im Umgang mit Unity, bin allerdings seit 4-5 Jahren in der Webentwicklung tätig. Um auch mal etwas dabei zu steuern, werde ich ein kleines Tutorial zum Thema PHP Keygenerator mit Unityintegration erstellen.

 

1. Was wird benötigt?

  • Webserver inkl. PHP und mySQL
  • Unity
  • Ein Spiel ( ;) )

2. Die Serverseite

 

Die Serverseite ist relativ einfach zu erklären. Sie besteht aus folgenden Teilen:

 

PHP Files:

  • config.php (Einstellen der Datenbank Daten sowie Keyoptionen)
  • generate_keys.php (Erstellung der Keys für das Spiel)
  • validate_key.php (Abfrage ob Key valide ist)

Datenbank:

  • Tabelle: keys
    • keys_id
    • keys_code
    • keys_used

Wir fangen mit der Datenbank an:

 

Die Datenbank beinhaltet, wie der Name schon sagt, die Keys. Ein Key enthält drei Daten hier:

 

keys_id:

Einzigartige ID welche jeden Key zuweisen lässt, ist hier eher unwichtig, allerdings sofern man etwas komplizeres macht in Form eines Systemes wo User Keys zugewiesen sind, ist sowas von nöten.

 

keys_code:

Der Code ansich, welcher nach euren Paramtern erstellt worden ist

 

keys_used:

Integer Wert welcher definiert ob der Key schon genutzt worden ist.

 

Kommen wir danach zu den PHP Scripts.

 

Die config.php definiert grob gesagt einfach wie was eingestellt ist, hier der kommentierte Inhalt:

<?php
//Open Database Connection
$db=new PDO('mysql:host=localhost;dbname=<DB>;',"<USER>",'<PWD>',
array(
	PDO::ATTR_PERSISTENT =-> true,
	PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ,
	PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
));

//Set Game Secreat (Avoids public access e.g.)
$gamesecret='V@w&Kp-c';
$internpw='unityforever';

//Key Settings
$keysetting['parts']=4; //How many parts the key has?
$keysetting['partlength']=5; //A part got how many chars?
$keysetting['splittedby']="-"; //Which char connect the parts?
$keysetting['keychars']="ABCEDFGHIJKLMOPQRSTUVWXYZ123456789"; //Which chars will be used?

 

Die config.php definiert wie ihr seht einige globale Einstellungen, sie wird am Anfang von den beiden anderen Dateien included.

 

Die Datei validate_key.php überprüft ob ein Key...

  • ...existiert
  • ...schonmal benutzt worden ist.

Und gibt in Abhängig davon einen Wert zurück welcher von Unity interpretiert werden kann.

 

Der Aufbau ist relativ einfach:

<?php
require_once("config.php");

//Is the correct gamesecret used? This is against random access e.g. for bruteforcing keys
if($_GET['gamesecret']==$gamesecret){

$query=$db->prepare("SELECT keys_id,keys_used FROM `keys` WHERE keys_code=:key");  //Search for Keys with this code
$query->execute(array(":key"=>$_GET['key'])); //Implement our Key in the Query (Used against SQL. Injections)
$data=$query->fetch(); //Fetch the result data
if($data===false){
	echo "Key doesn't exist."; //No entry was found
}else{
	if($data['keys_used']=="0"){
		echo "Login Success."; //The key exists and wasn't used yet
		$db->query("UPDATE `keys` SET keys_used='1' WHERE keys_id=".$data['keys_id']);
	}else{
		echo "Key already used."; //The key exists, but was used already
	}
}
}

 

Er fragt ab ob ein Key existiert und sofern er existiert, ob er bereits genutzt worden ist. Sofern er existiert und nicht benutzt worden ist, gibt er "Login success." zurück und beschreibt die Datenbank mit keys_used=1, somit ist der Key danach benutzt.

 

Als nächstes folgt unsere generate_keys.php:

<?php
session_start(); //Starts the Session Managment
require_once("config.php");

if(isset($_POST['pwd']) && $_POST['pwd']==$internpw){ //Was the loginform sended + the pw correct?
$_SESSION['logged']=true;
}

if(isset($_SESSION['logged']) && $_SESSION['logged']==true){ //Is the user logged in?
if(isset($_POST['amount'])){ //The form was sent?

	$keylist=""; //Create empty Var for all the keys
	$query=$db->prepare("INSERT INTO `keys` SET keys_code=:key"); //Prepare the SQL Query
	$secruityquery=$db->prepare("SELECT keys_id FROM `keys` WHERE keys_code=:key");

	$maxarrayid=strlen($keysetting['keychars'])-1; //How many chars we have in our keychars list?

	for($i=0;$i<intval($_POST['amount']);$i++){ //Create all keys
		$key="";
		//Loop for each part
		for($partsnow=0;$partsnow<$keysetting['parts'];$partsnow++){
			//Loop for each string of the part
			for($partlengthnow=0;$partlengthnow<$keysetting['partlength'];$partlengthnow++){
				$key.=$keysetting['keychars'][mt_rand(0,$maxarrayid)];
			}
			$key.=$keysetting['splittedby']; //Add the split for each part
		}
		$key=substr($key,0,-1); //Remove last splitchar

		//This is important now! Check if the key doesn't already exists
		$secruityquery->execute(array(":key"=>$key));
		$secruityresult=$secruityquery->fetch();

		if(!isset($secruityresult['keys_id'])){
			$query->execute(array(":key"=>$key)); //Add the key to the Database
			$keylist.=$key."\n"; //Add the key to the keylist
		}else $i--; //Add another loop to the Keycreation
	}

	file_put_contents("keys.txt",$keylist,FILE_APPEND);
}
?>
<!DOCTYPE html>
<html>
<head>
	<title>Keymanager - Create Keys</title>
	<meta charset="UTF-8">
</head>
<body>
<h2>Keymanager - Create Keys</h2>
<form method="post">
	<label for="amount">Amount</label>
	<input type="text" name="amount" id="amount"/>
	<input type="submit" name="submit" value="Add">
	<p>Keys will be added to keys.txt after creation.</p>
</form>
</body>
</html>
<?php
}else{ ?>
<!DOCTYPE html>
<html>
<head>
	<title>Keymanager - Login</title>
</head>
<body>
	<h2>Keymanager - Login</h2>
	<form method="post">
		<label for="pwd">Passwort</label>
		<input type="password" name="pwd" id="pwd"/>
		<input type="submit" name="submit" value="Einloggen">
	</form>
</body>
</html>
<?php }

 

Die Datei ist wohl am kompliziertesten, ich habe sie probiert so gut wie möglich zu kommentieren.

 

Sie hat wohl die wichtigste Aufgabe. Sie überprüft, ob der User eingeloggt ist oder nicht und zeigt ggf. die Loginmaske an, erstellt die Keys an Hand der Liste von Zeichen welche gegeben sind und verbindet diese zu einem Key.

 

Der erstellte Key wird überprüft, ob er nicht bereits schon in der DB existiert. Es ist unwahrscheinlich, aber man sollte es lieber im Vorraus vermeiden.

Sofern der Key nicht existiert, wird er hinzugefügt zur Datenbank und in der Keyliste abgespeichert.

 

Die letzte Datei ist die keys.txt, sie beinhaltet einfach alle erstellten Keys. Wichtig ist, dass diese einen CHmod von 777 hat! Sonst kann der Server sie nicht beschreiben!

 

Wer sich das ganze mal anschauen möchte:

Generator

Keyliste

 

Das war es erstmal zum Part der Serverseite, der Code wurde probiert relativ einfach aber natürlich sicher zu halten. Im zweiten Part, morgen oder übermorgen kommen wir dann zum Part in Unity.

 

Feedback oder Kritik ist gerne immer gesehen, da es mein erstes Tutorial war generell sowie zu Unity.

 

Viele Grüße,

Daniel

unity_tutorial_serverside.rar

  • Like 2

Share this post


Link to post
Share on other sites

3. UnityCode

 

Als nächstes folgt unser Code in Unity. Ich habe es um es einfach zu halten in eine Datei geschrieben, diese wird in der Loginscene der Maincamera angehangen.

 

using UnityEngine;
using System.Collections;

public class KeyCheck : MonoBehaviour {

public string validateKeyURL; //Enter your validate_key.php URL here
string productkey;
WWW request;
WWWForm form;


void OnGUI(){
	GUI.Box (new Rect(10,10,300,90),"Keycheck"); //Great Box for the Form
	productkey=GUI.TextField(new Rect(20,40,280,20),productkey,60); //Textinput
	if(GUI.Button (new Rect(20,70,280,20), "Check Key")){ //If Input pressed, send the form

		form = new WWWForm();
		// Add your fields
		form.AddField( "gamesecret", "V@wKp-c" );
		form.AddField( "key", productkey );
//Send the request
		StartCoroutine(GetKey());									  
	}

}

public IEnumerator GetKey() {
	request = new WWW(validateKeyURL,form);
	yield return request;
Debug.Log (request.text); //Show the answer to the log
}

}

 

Der Code holt den Input aus dem Textfield und sendet ein Request an die URL. Die URL

antwortet dann mit Login success o.Ä. und gibt wieder, ob der Key korrekt war.

 

Anstatt Debug.Log(); könnt ihr hier natürlich auch folgendes machen:

public IEnumerator GetKey() {
	request = new WWW(validateKeyURL,form);
	yield return request;
if(request.text=="Login Success."){
    Application.LoadLevel(1);
}else{
    //Error Handling
}
}

 

Hier wird sofern der Key korrekt war ein anderes Level geladen, die Möglichkeiten sind da offen.

 

Fragen? Kritik? Sonst etwas? Gerne könnt ihr Vorschläge posten, was ich beim Tutorial verbessern kann oder beim nächsten Beachten, würde mich sogar freuen.

 

Schönen Abend noch!

Share this post


Link to post
Share on other sites

Moin moin!

Ein paar Fragen und Anregungen habe ich doch da gleich.

 

1. Warum das Ganze als Webanwendung machen und riskieren, dass irgendwer da rankommt? Auch wenn das eher für kleine Spiele benutzt werden wird...

Ich tendiere da eher zu einer Lösung mit einer Windows Forms-Anwendung oä.und würde PHP nur als Interface nutzen. Also etwas, was im Idealfall nicht aus der Hand oder ins www gegeben wird, sondern nur vermittelt. Den KeyGenerator als Website zu lösen mag plattformübergteifender sein, aber wann ist man schon unterwegs und denkt sich: ich könnte doch mal ein paar Keys generieren ;)

 

2. Warum die txt, die ja auch noch auszulesen ist? Mit Schreibrechten? Es sind doch alle Keys in der db und im Idealfall vor ungebetenem Besuch geschützt;) Davon abgesehen wäre es sinnvoll sie zuerst in eine lokale Datenbank und anschließend in die des Servers zu übertragen.

 

3. Ich würde die ID nicht für den Key, sondern als GUID (global unique id) für den Spieler und/oder Account nutzen. Bin mir nicht sicher ob du das vor hattest. Die ID wird zwar erwähnt, aber ein Fallbeispiel, ein warum und wofür du sie im konkreten Fall nutzen würdest, fehlt mir, daher der Vorschlag.

 

4. Zum Thema Sicherheit. Schön wäre es doch, wenn du das pw noch verschlüsseln würdest etc pp. Gehört für mich schon dazu. Schön mit einem langsamen Algorithmus, der den HashKey aus pw und pepper checkt. Sowas sollte man sich doch eh angewöhnen, spätestens, wenn du irgendwas aus Unity heraus durch die Gegend schickst, sollten da kein Klartext-Zeugs rumfliegen, sprich dein oder HashKey im Klartext übermittelt werden. Das ist weder in Unity, noch in PHP allzu großer Aufwand und sollte in diesem Tutorial unbedingt passieren, da sonst all die, die aus diesem Tutorial lernen, unsichere Sachen lernen. ...und strings escapen, bitte ;)

 

5. Da ich PHP eig nur als Schnittstelle nutze und sonst nicht mehr so webentwicklermäßig unterwegs bin, mal die Frage:

Was hat PDO für Vorteile?

Ich mache das eig immer so mit mysqli

(config.php)

<?php
$db_server   = 'localhost';
$db_user   = 'root';
$db_pass   = 'superMasterPassword';
$db_name   = 'dbName';
error_reporting(E_ALL ^ E_NOTICE);
@$mysqli = new mysqli($db_server, $db_user, $db_pass, $db_name);

?>
(settings.php)
<?php
   $pepper         = "ydf7byd7fb798ydfb789";
   $serverVersion     = "0.1-Build-0001";
   $real_hash         = hash("sha512",$serverVersion . $pepper);
   if (isset($_POST["hash"]))
   {
       $hash = mysqli_real_escape_string($mysqli, $_POST['hash']);
   }
//usw
?>

<?php
ob_start();//Hook output buffer
include("../config.php");
include("settings.php");
include("character.php");
include("account.php");
ob_end_clean();//Clear output buffer

if (mysqli_connect_errno())
{
   die('<h1>Could not connect to the database</h1><h2>Please try again after a few moments.</h2>');
   $mysqli->set_charset("utf8");
}
else
{
   if($maintenance == false && $real_hash == $hash)
   {
    if ($result = $mysqli->query("SELECT * FROM $rpg_character WHERE guid ='$id' "))
       {
           while ($row = $result->fetch_object())
           {

               $stats = array
               (
                   'race'            =>$row->race,
                   'gender'        =>$row->gender,
                   'factionside'    =>$row->factionside,
                   'level'            =>$row->level,
                   'class'            =>$row->class,
                   'xp'            =>$row->xp,
                   'expTotal'        =>$row->expTotal,
                   'health'        =>$row->health,
                   'power'            =>$row->power

               );
               echo json_encode($stats); // needs to be encoded
           }
       }
       $result->close();
       $mysqli->close();

   }
}
?>

Share this post


Link to post
Share on other sites

Hi CodeMonkey,

 

Danke erstmal für dein Feedback!

 

1. Das mag im Endeffekt geschmackssache sein, es mag mit Sicherheit sinnvoller sein, jap. Damit kann ich aber leider nicht dienen, da ich in der Hinsicht keine Erfahrung habe :) Man kann die PHP Datei natürlich auch einfach runter nehmen.

 

2. Damit die Leute die Keys auch sehen können, die sie generieren ;) Das ist ein Tutorial, ich möchte ja erstmal zeigen wie die Basics gehen. Wie man später damit umgehen kann, um die Keys als PDF via Mail zu bekommen z.B. würde den Umfang sprengen. Die Keys müssen ja auch irgendwie weiter gegeben werden.

 

3. Die ID ist dafür da, dass wenn du z.B. später nen User hast also Tabelle user ihm die ID zuweisen kannst und somit user mit id verbinden zu können, Key mit User fände ich unpraktisch. Aber ich werde da gerne noch ein Beispiel reinhauen, guter Vorschlag!

 

4. Strings werden escaped, das wird in PDO mit bind_param gemacht, bevor du mir sowas Vorhalten willst ;) Encoding kann ich später gerne noch nachtragen, aber eher in einem dritten Teil, die Idee ist aber Sinnvoll.

 

5. Ob mySQLi oder PDO hat ansich keine großen Vorteile, PDO unterstützt mehrere Datenbank Typen z.B.

http://net.tutsplus....should-you-use/

 

Da haste ne fixe Tabelle dazu.

 

VG.

Share this post


Link to post
Share on other sites

Jupp, dass wird sicher den Rahmen sprengen.

 

Naja, wie man das wegen dem Zuordnen macht... würde da, wie du sagst, dem User die Keys zuordnen, jedoch ohne die key_id, weil ich die überflüssig finde. Es reicht doch völlig <user1><key1> --- <user1><key2> --- <user2><key5343434614>...usw. ...aber auch das ist wohl Geschmackssache.

 

Da muss ich mir PDO nochmal ansehen. Hab nach PHP 4 den Sprung auf 5 nicht so 100% gemacht, weil ich´s nie so richtig brauchte.

Share this post


Link to post
Share on other sites

Naja, du kannst entweder der Tabelle keys einen Spalte user_id geben oder users eine spalte keys_id, das wären ja die zwei Wege.

Was du davon machst, ist denke ich frei. Ich mach lieber Sachen von der user_id abhängig, weil ich die meist in der $_SESSION speicher.

 

Ich denke das ist Geschmackssache mit PDO oder mySQLi. Habe mit beidem mal gearbeitet, finde beides gut.

Share this post


Link to post
Share on other sites
Am ‎22‎.‎07‎.‎2013 um 22:00 schrieb Schalla:

Um auch mal etwas dabei zu steuern, werde ich ein kleines Tutorial zum Thema PHP Keygenerator mit Unityintegration erstellen.

Ich bin ebenfalls neu in der Unity Szene. Ich lerne auch gern neues, darum würde ich mal gerne fragen: Was ist genau ein Keygenerator? Was könnte man theoretisch alles mit anstellen. So wie ich das ganze jetzt verstanden habe ist das nämlich einfach nur ein Zähler, der jedem Spieler eine gewisse Identität gibt und diese benutzt wird ihn eindeutig identifizieren zu können.

 

Grüße :)

Share this post


Link to post
Share on other sites
Am 15.5.2017 um 15:50 schrieb checkalord3:

Ich bin ebenfalls neu in der Unity Szene. Ich lerne auch gern neues, darum würde ich mal gerne fragen: Was ist genau ein Keygenerator? Was könnte man theoretisch alles mit anstellen. So wie ich das ganze jetzt verstanden habe ist das nämlich einfach nur ein Zähler, der jedem Spieler eine gewisse Identität gibt und diese benutzt wird ihn eindeutig identifizieren zu können.

 

Grüße :)

Um zum besipiel Gamekeys zu generieren.

Wenn du dei Spiel nicht über steam oder so vermarkten willst, kannst du so selbst dafür sorgen das dein Spiel nur mit gültigem Key spielbar ist.

Oder um Keys für einen Ingameshop zu generieren. Deiner Fantasie sind da keine Grenzen gesetzt.

Share this post


Link to post
Share on other sites

Läuft aber nicht mehr Bekomme nur diesen Fehler:

 

NullReferenceException: Object reference not set to an instance of an object
UnityEngine.GUI.DoTextField (Rect position, Int32 id, UnityEngine.GUIContent content, Boolean multiline, Int32 maxLength, UnityEngine.GUIStyle style, System.String secureText, Char maskChar) (at C:/buildslave/unity/build/Runtime/IMGUI/Managed/GUI.cs:629)
UnityEngine.GUI.DoTextField (Rect position, Int32 id, UnityEngine.GUIContent content, Boolean multiline, Int32 maxLength, UnityEngine.GUIStyle style, System.String secureText) (at C:/buildslave/unity/build/Runtime/IMGUI/Managed/GUI.cs:616)
UnityEngine.GUI.DoTextField (Rect position, Int32 id, UnityEngine.GUIContent content, Boolean multiline, Int32 maxLength, UnityEngine.GUIStyle style) (at C:/buildslave/unity/build/Runtime/IMGUI/Managed/GUI.cs:611)
UnityEngine.GUI.TextField (Rect position, System.String text, Int32 maxLength) (at C:/buildslave/unity/build/Runtime/IMGUI/Managed/GUI.cs:491)
KeyCheck.OnGUI () (at Assets/Menü/Scripte/KeyCheck.cs:19)

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×