Network Server Setup

CS 493 Lecture, Dr. Lawlor

Servers need a working network connection.  Virtualbox networking defaults to a disconnected network, which is ideal for malware analysis, but useless for testing a server.  (Ubuntu server actually has a 5 minute timeout on startup if it doesn't detect a network, which is designed to wait out short network outages.)

Servers also need an externally routable IP address.  This is not the default on most networks, which protect their clients behind a firewall that does "Network Address Translation (NAT)" between external IP addresses (at UAF, 137.229.x.x) and internal IP addresses (at UAF, 172.20.x.x).  In a virtual machine, NAT mode is useful to fetch packages from the real network, but it's tricky to run a server behind NAT; you need bridged (the grown up solution which puts the VM on the real network, but it only works with a hardwired connection on the host), or host-only networking (which only lets you talk to the host, ideal for testing your server).

I normally write servers using C++, and production servers run a huge variety of languages (Java, C#, Python), but for this class I'd like to start very simple so I'll be giving examples in PHP. 

Setting up Apache and PHP

PHP is a programming language whose output is a web page.  It's designed to sit behind a web server, traditionally Apache (hence the LAMP Stack: Linux, Apache, MySQL, PHP). 

Task
Linux
OS X Windows
Open sysadmin shell
Open a Terminal
sudo su
Open a Terminal
sudo su
Start -> CMD
Right click, "Run as Administrator"
Install PHP behind Apache
Ubuntu:
 sudo apt-get install php-common libapache2-mod-php php-cli
XAMPP has an easy to use installer for Windows and OS X.  (MariaDB is an open source fork of MySQL.)
Check IP address
ifconfig
ifconfig
ipconfig
Check running services
netstat -tulpan
netstat -ap tcp netstat -bano
Restart apache
service apache2 restart
Use XAMPP control panel, or
apachectl stop
cd C:\xampp\apache\bin
httpd -k -restart
Apache config files
/etc/apache2
xampp/apache/conf
Apache web content directory (by default)
/var/www/html
xampp/htdocs

If apache is running correctly on your VM, you should be serving a web page from the content directory.  From inside the VM, "wget localhost" or navigate to http://127.0.0.1 should get that page.

If you switch your VM over to host (or bridged) networking, you should be able to access the same apache server page from your host.  My VM got the host-only IP address 10.0.10.101, so I can visit http://10.0.10.101 to see my page.

If I modify the index.html file in the Apache web content directory, Apache will immediately start serving the new page.  You may need to hit shift-reload to get your browser to actually request the new page though. 

PHP Language

PHP source code is designed to be interspersed with literal text.  This makes it very easy to start with a static web page, and then insert some PHP in the middle to generate dynamic elements. 

NetRun is missing all the PHP modules, but the basic simplified-perl syntax works there:
Free form text here!
<?php
$start=microtime(true);
for ($i=0;$i<10;$i++) {
	print "i=$i\n";
}
$elapsed=microtime(true)-$start;
print "Elapsed=$elapsed seconds";
?>

More text down here!

(Try this in NetRun now!)

In your VM, move away the index.html in your apache web content directory, and make a file named "index.php" with something simple like this:
Welcome:
<?php

print "PHP works!  ";

?>

You should see both the plain text and the PHP code printed text.  If you:
The best feature of PHP is it's quite easy to connect to a database (I recommend using the portable PDO method).
<?php

$dsn = "mysql:host=localhost;dbname=webstore;charset=utf8";
$user="storemaster"; // database user
$pass="LMAO"; // database password

$conn=new PDO($dsn,$user,$pass);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
print "<p>DB connection established.";

$sql="SELECT * FROM customers";
foreach ($conn->query($sql) as $row) {
echo "<p>Customer: ID $row[0], name $row[1], addr $row[2]";
}

?>
You can also send CGI parameters to a PHP script, and extract them via the $_REQUEST array.  For example, if you add "?
foo=bar" to the end of the URL, then $_REQUEST["foo"] in PHP would be the string "bar".
<?php

print "The value of foo='" . $_REQUEST["foo"] . "'.";

?>

SQL Injection

In PHP both CGI parameters and SQL are pretty easy.  But combining parameters and SQL requests is a lot trickier than it looks.  It's actually super dangerous if you do the obvious thing and assemble an SQL query using the user-input parameter as a string, like this:
$sql="SELECT * FROM customers WHERE name='" . $_REQUEST["name"] . "'";
Then suddenly somebody clever can add CGI parameters (or POST parameters, or a cookie, etc) that end the quote much too early, like:
	injectable.php?name=%27%20OR%201=1%20OR%20name=%27
This is the URL escaped version of:
' OR 1=1 OR name='
Which when stuck together with the original query gives:
SELECT * FROM customers WHERE name='' OR 1=1 OR name=''
Because 1=1 on every row, this displays the whole customer database. 

By adding a semicolon, you can inject a string of unrelated commands, like this UPDATE:
injectable.php?name='; UPDATE customers SET address="yup" WHERE id=1;
And it gets worse:

Did you really name your son Robert;); DROP TABLE Students; --?   Oh, yes.  Little bobby tables, we call him.

The fix is to either (1) manually escape *every* occurrence of a string before it hits the database, or (2) use PDO prepared statements, like this:
$query=$conn->prepare( "SELECT * FROM customers WHERE name=:name" );
$query->execute(array( ":name" => $_REQUEST["name"] ));

foreach ($query as $row) ... as before ...
The ":name" business is a placeholder used during prepare, and substituted during the execute.  Crucially, PDO can parse and validate the request before substitution, and then systematically escape the parameters as strings during execution.  It's only a tiny amount of additional code, and it doesn't hand the whole database over to anybody smart enough to be able to modify the URL bar in their browser!

Here's the complete PHP example, which should be stored in a .php file, and passed a CGI parameter like ?name=Walter White
Welcome to the store:

<?php
$dsn = "mysql:host=localhost;dbname=webstore;charset=utf8;";
$user = "storemaster";
$pass = "LMAO";

$conn=new PDO($dsn,$user,$pass);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
print "<p>DB connection established.";

$query=$conn->prepare( "SELECT * FROM customers WHERE name=:name" );
$query->execute(array( ":name" => $_REQUEST["name"] ));

foreach ($query as $row) {
echo "<p>Customer: ID $row[0], name $row[1], addr $row[2]";
}

echo "<p>Store done;"
?>

See Shar & Tan 2013 for paper-length details on countermeasures against SQL injection.  (It's this week's CS 693 paper.)