EGL Development User Group - Group home

How to properly waste your time using EGL

  
Today, I wrote a game in EGL Rich UI inspired by the well-known web game called "Escape", "Escapa", "The Red Box Game", or whatever name the latest inspired copier decided to give before hosting it on their own homepage. I am still trying to find out who really wrote the original.


The intent of the game is simple. You drag the red box labeled "EGL" around with your mouse. All you need to do is avoid the blue boxes and the walls.

How does the game work?

The game essentially consists of two parts: the UI that draws the boxes and let you drag the red box and a service component that can show the top 10 scores. I will describe the two parts in more detail now.

How to avoid Java, C, PHP, and JavaScript

The main algorithm for this game is the following:
  • place a red box in the middle and allow the user to drag it
  • place four blue boxes elsewhere and give each a unique motion vector
  • start a new job to run each X milliseconds to move each blue box
  • when the red box goes outside the white box or overlaps with a blue box, stop
  • every 10 seconds decrease the value of X so the blue boxes will move faster

The blue boxes shown in the UI are positioned absolutely using a syntax like this:
    new Div {
 position = "absolute", x = 300, y = 330,
 width = 100, height = 20,
color = "white", background = "navy", font = "Arial",
innerHTML = "<center style='margin-top: 1px'>JavaScript</center>" }


By making the boxes positioned absolutely, we can place them at any place we want. For this box, the initial location is at x=300 and y=330.

Each time the game timer goes off, we move the blue boxes in a given direction:
    moveEnemy(enemies[2], -12, -20);


For each blue box, we remember its x and y direction. For instance, when it bounces against the right wall, we make its x direction -1. The box is moved simply by giving it a new x and y coordinate. After moving the box, we check to see if we now touch the red box. If so, we stop the game.
    function moveEnemy(enemy Div, x int in, y int in)  
directionX int = enemy.getAttribute("directionX");
directionY int = enemy.getAttribute("directionY");
if (touchesVerticalWall(enemy))
directionX *= -1;
enemy.setAttribute("directionX", directionX);
 end
if (touchesHorizontalWall(enemy))
directionY *= -1;
enemy.setAttribute("directionY", directionY);
end
enemy.x += x * directionX;
enemy.y += y * directionY;
if (enemyTouches(enemy))
stop("a blue box");
end
end


The red box itself has drag and drop handlers define on it:
    red Div {   
onStartDrag = startDrag, onDrag = drag, onDropOnTarget = endDrag,
cursor = "pointer",
position = "absolute", x= 205, y = 205, width = 40, height = 40,
background = "darkred", color = "white", font = "Arial",
innerHTML = "<center style='margin-top: 9px'>EGL</center>"
};


This allows us to start the game and drag the red box while the user moves the mouse:
    function startDrag(widget Widget in, x int in, y int in) returns(boolean)  
dx = x - red.x;
dy = y - red.y;
start();
return (true);
end
function drag(widget Widget in, target Widget in, x int in, y int in)
 if (!stopped)
red.x = x - dx;
red.y = y - dy;
end
if (outsidewhite())
stop("the black border");
end
 end

function endDrag(widget Widget in, target Widget in, x int in, y int in)
end


Finally, when the game stops, we inform the user and display the top 10 scores:
    function stop(reason String in)  
message.text = "You touched "+reason+". Drag the red box to start.";
reset();
stopped = true;
top10.score = (browser.currentTimeMillis() - startTime)/1000;
end


Keeping a Top 10 List

Our top 10 list consists of a little bit of UI, and a service call to report the current score and receive an answer back with the total number of games played and the top 10 games.

The UI is nothing thrilling, other than using a cookie to remember your name:
    nameLabel TextLabel { text = "Your Name: " };   
cookie Cookie { key = "name" };
nameField TextField { width = 80, text = cookie.value, onKeyUp ::= setName };


That same cookie is updated when the user enters a new name, and stored in the browser's cookie jar. Next time the user returns, the stored value will be automatically picked up. Updating a cookie value is as easy as giving it a new value:
    function setName(e Event in)    
if (nameField.text != "")
cookie.value = nameField.text;
end
end


The service calls a REST service with 2 URL parameters. It returns a record with inside it an array of top scores. Notice from the definition below that it is not relevant if the service is using XML or JSON to encode its contents. The EGL runtime will determine at runtime how to parse the result.

The service definition looks like this:
    interface HighScores    
function getTop10(name String in, score float in) returns(HighScoresRecord)
{@GetRest {
uriTemplate="http://chrislaffra.com/escape/highscore.php?name={name}&score={score}"
}};
end
record Result
total int;
top10 ScoreRecord[0];
end
record Score
name String;
value float;
end


To call the service, we use the EGL call statement identifying the service we want to call and what function we want to use when the response arrives in the future:
    function setScore(score float in)    
this.score = score;
scoreField.text = "Score: "+score;
call svc.getTop10(nameField.text, score) returning to handleTop10 onException ServiceLib.serviceExceptionHandler;
end


The top 10 scores are stored in a div that is given an opacity of 0.8, so it is a little bit transparent:
    top10 Div {      
position = "absolute", x = -100, y = 5,
*opacity = 0.8*, padding = 10, background = "black",
borderColor = "red", borderWidth = 12, borderStyle = "solid"
};


When the answer comes back from the top10 service it is converted automatically by EGL into a set of nested records and we simply add them to our top10 div. Finally, we show the top10 div in the right position:
    function handleTop10(result HighScoresRecord in)    
s String = "Total plays: <b>"+result.total + "</b>. " +
"Your score: <b>"+score+"s</b>.<p>" +
"Here are the Top 10 scores:
" +

"<table border=1 width=300 cellpadding=3 style:'margin:10px; opacity: 1.0'>";

for (n int from 1 to result.top10.getSize())

score ScoreRecord = result.top10[n];

s += "<tr><td>" + n + "</td><td>" + score.value +

"s</td><td>   " + score.name + "</td></tr>";

end

s += "</table>";

top10.innerHTML = s;

top10.x = scoreField.x + scoreField.pixelWidth + 15;

ui.table.setAttribute("valign", "top");

showTop10(true);

end

function showTop10(value boolean in)

if (value)

ui.appendChild(top10);

else

ui.removeChild(top10);

end

end



Using PHP to implement the backend service

All the client code is done in EGL. I decided to place the game on an ISP where I took the cheapest plan available. The plan comes standard with PHP and MySQL. So, I decided to try and use MySQL and PHP to store the high scores to see how that worked.

The MySQL table structure is pretty simple:
    name    varchar(64)            
score float
ip varchar(32)


I added and IP field to remember where the service came from (which is of course a very limited approach to authentication). I created the table using the phpMyAdmin client that my Internet provider offers.

Then I wrote some PHP and placed it right next to my game's index.html file. Here is what the (insecure) PHP looks like:
<? 
$host = "localhost";
$user = "*********";
$pass = "*********";
$db = "highscores";

$name = $_REQUEST['name'];
$score = $_REQUEST['score'];
$ip = $_SERVER['REMOTE_ADDR'];

$connection = mysql_connect($host, $user, $pass) or die ("Unable to connect!");
mysql_select_db($db) or die ("Cannot open database $db");

if (isset($name) && isset($score)) {
$query = "INSERT INTO highscores(name, score, ip) VALUES('$name', '$score', '$ip')";
$result = mysql_query($query);
}

$query = "SELECT * FROM highscores";
$result = mysql_query($query);
$total = mysql_num_rows($result);
$query = "SELECT * FROM highscores ORDER BY score + 0 DESC LIMIT 10";
$result = mysql_query($query);
echo "<result><total>$total</total>";
if (mysql_num_rows($result) > 0) {
while($row = mysql_fetch_row($result)) {
echo "<score><name>$row[0]</name><value>$row[1]</value></score>";
}
}
echo "</result>";
mysql_close($connection);
?>


Security

My PHP code is highly insecure for various reasons. First, anyone can simply call the URL above and assign any score they like:


Note that I added some sanity checks so that the above URL no longer works

Using a REST service with URL parameters makes it trivial to call in a browser.

Some ways of making service hacking more difficult would be:
  • Use a POST and place the arguments in the message body, instead of on the URL.
  • Use some "secret" encoding for the message that only the client and the service know about.
When you use SOAP messages, you already use POST and a secret encoding (in this case a SOAP envelope).

One complicating problem is that our client is written in easy to read JavaScript (compiled from our EGL). That gives hackers a lot of leverage. However, even extreme obfuscation of the JavaScript won't help as hackers can easily use Firebug in Firefox to watch the network traffic and derive what services are used and how to call them. Then they can play the man-in-the-middle game to make the server believe they are the client.

Maybe JavaScript obfuscation combined with service obfuscation has a higher degree of chance of making hackers give up and look for an easier target to hack.

Of course, obscuring thing stops the really silly hacks and even the accidental ones. However, as Andy Tanenbaum used to say when I was stil back in school attending his class on Operating Systems: obscurity is the worst form of security.

Always use SLL and proper authentication to increase security.

Securing the channel and authentication helps to a certain degree. In the end, however, services have to be extremely defensive, paranoid, trace everything, apply statistical analysis, and allow for easy human intervention. Think of your credit card company calling you on your cellphone to verify a suspicious transaction. Also, most comment sections on website have a button to report spam messages or abuse. Any system that runs over the Internet should be assuming the worst and be able to easily recover from abuse.

SQL Injection

One special form of disruptive hacking is to try and make the service run arbitrary code on the hacker's behalf. In our PHP example shown above, we allow for SQL injection very easily.

Then main attack vector in our example is given here:
   $name = $_REQUEST['name'];  
$score = $_REQUEST['score'];
...
$query = "INSERT INTO highscores(name, score, ip) VALUES('$name', '$score', '$ip')";


We retrieve $name and $score from the request URL, and compose a query out of it. If we pass in 'name=Chris' and 'score=32.351', the resulting query would be:
    INSERT INTO highscores(name, score, ip) VALUES('Chris', '32.351', '192.168.0.1')";


Now, assume someone passes the following parameters to the REST service:
    name=x',1,foo); DROP TABLE highscores; --


Our query would then look like this:
    INSERT INTO highscores(name, score, ip) VALUES('x',1,foo); 
DROP TABLE highscores; --', '32.351', '192.168.0.1')";


As "--" starts a comment in SQL, the query now becomes the following two separate SQL statements:
    INSERT INTO highscores(name, score, ip) VALUES('x',1,foo);   
DROP TABLE highscores;


By naively allowing any arguments to be passed to us we allowed random SQL code to be executed on our database. Hackers do not need access to the PHP to come up with this, as automated tools are available to try random fragments until they find one that works.

The simplest solution is to not allow for the script to insert the ';' character. Namely, without that the SQL injection cannot insert a second statement. To avoid further unintentional modifications to the original select statement, we can also check for the single quote character.

To help fight SQL injection, there is a simple PHP trick to sanitize URL parameters. It looks like this:
   $name = mysql_real_escape_string($_REQUEST['name']); 
$score = mysql_real_escape_string($_REQUEST['score']);


I added that to the PHP that is currently used by the published game.

Monitoring

In our sample above, we do not monitor client access, other than storing the IP address. Additional information may be collected and stored, such as host name, referer page, browser agent, timestamp, etc. Basically, you should record anything that may help the FBI solve your case once someone drained your server from all its data.

How to really solve security problems

Basically the advise is: Don't try this at home, kids!

Enterprise organizations should not use adhoc security models, of course. The recommendation is to use something like Tivoli Access Manager to control access to, and monitor, their services.

Furthermore, instead of using PHP, you should really write your services in EGL, compile them to Java, and run them on WebSphere, which is proven to be much more secure for Enterprise applications. Using EGL will also make it easier for you to write the service as you will not have to struggle with the quirky syntax of both PHP and MySQL.