I'm using (an adaptation of) the online highscores table example from the Unify Wiki in a project of mine. It worksf just ine when I run it in the Unity editor - saves the scores to the MySQL database and so on. The thing is, when I run the built Unity.webapp on the server, it doesn't write to the database anymore. Anyone have any idea what might cause that sort of wierdness to happen?
From a little bit of testing I have just done, it seems that firstly, the web player can only communicate with a PHP script in the folder it is being served from (ie, the web player file and the PHP script must be in the same folder on the server) and secondly, the web player identifies itself as the browser, not as Unity Player. Check that your PHP script is in the same folder as your player and remove the "if" statement from the PHP that looks like this:- Code (csharp): if ($_SERVER['HTTP_USER_AGENT'] == "UnityPlayer/1.1 ([url]http://www.otee.dk[/url])") (I am assuming you are referring back to the example from your earlier post.) However, without this "if" statement, the script will accept updates from anywhere - it is there to stop people falsifying high scores. If you want this protection for the web player then you will need a second PHP script that responds to the web player separately. This script is similar but checks that the referring page is the one that houses your player rather than querying the user agent. You would replace the "if" statement above with something like:- Code (csharp): if ($_SERVER['HTTP_REFERER'] == "([url]http://www.marty.com/UniGame/unigameHolder.htm[/url])") { ... } Note that both of these forms of protection are very weak - you would need something a bit more sophisticated if you really want to ensure the scores are valid.
Okay, Andeeee. I'm getting close. I removed the IF statement altogether and everything works fine. Of course, as you inform me, I cannot deploy like that because it would open me up to DS attacks, spoofing and gingivitis. The thing is, I've tried several variations of the IF statement, all to no avail. If the IF is there, it does not work. Apparently, I am not writing the user agent stuff correctly.
It's best to secure score sending by using some kind of checksum. More info: http://forum.unity3d.com/viewtopic.php?t=5462 http://forum.unity3d.com/viewtopic.php?t=1628
Good deal! I ripped out the referrer stuff altogether and went with a combination of (1) using and included db.inc.php file to secure my database login info, and (2) using MD5 encryption of the player/score data being passed from the game to the server. Now, everything appears to work perfectly as well as securely. There's event a hint of minty freshness in the air now! Thanks again, everyone!
Maybe someone should update the script on the wiki to work correctly with the web player. The checksum method seems like the way to go.
Actually, I intend do just that. I want to clean everything up and make it more presentable first though. Which means it will have to wait until after Top Dawg!
I've just updated the wiki page so it uses a shared secret method of authenticating the client using an md5 checksum.
Freyr, I'm curious. Isn't all the information necessary (for a spoofer) to pass bogus high scores to the high scores table right there in the addScore.php file - namely, the secretkey and hash?
Yup. That's why you have to modify the secret key to something different. The spoofer should not have access to the source of the add_score.php script.
Maybe this is the big point I'm missing. Isn't the addScore.php script right there on the website where anyone can download and look at it?
Only the output generated from it. PHP scripts are executed on the server, so the cllient should never see the actual code inside.
The website is only as secure as its security. If someone is in a position to access the unprocessed PHP code then you can't do much about it. However, if you make full use of the web server's security then it should be difficult for an outsider to gain this kind of access. Also, another trick is to store the secret data in a file outside the web folder and load it into the PHP script at runtime (ie, the data isn't stored inside the script) - this is a nice little bit of "insurance" against security lapses and oversights. Basically, if you switch on all straightforward safety features then it generally won't be worth an attacker's while to try and break them. At least not unless you are offering a big cash prize for the highest score!
How are they going to do that without ftp access? PHP isn't like Javascript where you can just look at the page source and see the code. (Server-side vs. client-side.) --Eric
They could use a site-ripper (i.e. WebDevil, DeepVacuum) to download the root and all sub- and remote- directories used by the game, with no more a-priori knowledge than the URL of the game itself. The stinking bastards.
These tools will only download the *output* of the PHP scripts - they communicate with the web server via HTTP so it's only the same as making lots of separate page requests from the browser. It's much more difficult to fool the server into coughing out the unprocessed PHP code (usually the result of bad configuration and quite easy to spot during testing). Also, if you use the technique I mentioned (put any secret data in a file outside the web folder) then it won't do the attacker any good even if they *do* manage to view the PHP. There shouldn't be any way they can get at the secret unless they have SSH access or a key to the server room and the right password. If they do have these then you've likely got much worse things to worry about than false high scores.
They can do no such thing. "Site-rippers" can only access the site the same way as any other anonymous user can. They have no magical powers to somehow divine your login and password. They have no ability to read directories that have permissions set to 711 or do anything else that someone with no direct access to your site can't do. --Eric
My lacking knowledge of web technology has bitten me soundly in the butt. I didn't realize that PHP was rendered server-side, thus leaving only fully hashed-out PHP for the rippers. Very cool indeed! Thanks, all!
Okay, now I've got a new wrinkle in this ongoing dilema ... My MD5 hash values, as generated by the Unify Wiki Unity Javascript and PHP, are not matching. Here's my Unity calling code: Code (csharp): // do md5 security check var hash = md5.Md5Sum(playerName + score + "secretkey"); // build WWW class call string, starting with server URL var highscore_url ="http://something.com/addScore.php?"; // add passed info to stirng highscore_url += "name=" + playerName + "&score=" + score + "&hash=" + hash; // WWW call hs_post = WWW(highscore_url); And here's my PHP calling code: Code (csharp): <?php $secretkey = "secretkey"; $name = $_GET['name']; $score = $_GET['score']; $hash = $_GET['hash']; $real_hash = md5($name + $score + $secretkey); // get login info require_once("includes/db.inc.php"); if ($real_hash == $hash) { // get database login info from secure file and connect to database $query = "insert into scores values (NULL, '$name', '$score');"; $result = mysql_query($query) or die('Query failed: ' . mysql_error()); } else { $name = $hash; $query = "insert into scores values (NULL, '$name', '$score');"; $result = mysql_query($query) or die('Query failed: ' . mysql_error()); $name = $real_hash; $query = "insert into scores values (NULL, '$name', '$score');"; $result = mysql_query($query) or die('Query failed: ' . mysql_error()); } ?> Any ideas, suggestions or corrections to my inestimable stupidity would be greatly appreciated! ;-)
The PHP script is attempting to use the plus operator for string concatenation. The concat operator is actually dot "." in PHP:- Code (csharp): $real_hash = md5($name + $score + $secretkey); It was actually doing (meaningless) arithmetic on the values. The wiki script was wrong, it wasn't anything you did. Actually, now I have looked at the wiki version of addscore.php, I have noticed it also contains a pretty serious SQL injection vulnerability. The wiki doesn't appear to be responding at the moment, but I'll post the fixed version of the script as soon as possible.
Yes, good idea:- Code (csharp): <?php $db = mysql_connect('mysql_host', 'mysql_user', 'mysql_password') or die('Could not connect: ' . mysql_error()); mysql_select_db('my_database') or die('Could not select database'); // Strings must be escaped to prevent SQL injection attack. $name = mysql_real_escape_string($_GET['name'], $db); $score = mysql_real_escape_string($_GET['score'], $db); $hash = $_GET['hash']; $secret_key="mySecretKey"; # Change this value to match the value stored in the client javascript below $real_hash = md5($name . $score . $secretKey); if($real_hash == $hash) { // Send variables for the MySQL database class. $query = "insert into scores values (NULL, '$name', '$score');"; $result = mysql_query($query) or die('Query failed: ' . mysql_error()); } ?> (Without the escapes, it might be possible for someone to do serious damage to the database, see information they shouldn't, etc... Ideally, the game should warn the user not to enter anything but letters, numbers and hyphens to avoid surprises.) The wiki page seems to go crazy when I try to replace the existing code with this, but I'll see if I can update it somehow.
So, another way to avoid this potnetial problem would be to not allow the user to enter anything orther than alphanumeric characters (and maybe a few other safe characters) when they enter their name. Correct?
No, because even if you secure things on the Unity side, someone could still attack from outside of your Unity game/app. Say, with a web browser or curl from the command line. Cheers, -Jon
Dangit. Good point, JC. Well, I finally got it all to work - unlike merely appearing to work, as I had previously. Tons of thanks to all of you friendly reply-ers!