PHP: SQL Injection

To follow up on my previous post about user input in PHP I’m now going to discuss one of the biggest issue in PHP which is SQL Injection vulnerabilities.

SQL Injection is when an end user can run arbitrary SQL commands and even shell commands. There are various way to mitigate this issue, the best solution is to use PDO to interact with the database using parameterize SQL Queries.

Here is an example of some SQL Injection vulnerable code:

<?php
$db->query("SELECT * FROM tags WHERE name='" . $_GET['tag'] . "'");

Now how can I inject some SQL into this Query. Here are some examples of the value of $_GET['tag'] that will inject some SQL.

$_GET['tag'] = "';DROP TABLE users"; // This will escape from the orginal query and then drop the users table
$_GET['tag'] = "';SELECT * FROM users"; // This will get all the user records from the users table
$_GET['tag'] = "';\! rm -rf /'"; // Delete all files on the server

As you can see SQL Injection can cause some bad things.

There are 3 things you should do when setting up a database for your PHP application:

  1. Create a database user just for that application
  2. Only give that database user access to the database for the application
  3. NEVER USE THE ROOT DATABASE USER FOR YOUR APPLICATION

The best way to mitigate SQL Injection is to use PDO with parameterize SQL queries, like so.

<?php
$db = new PDO('host=localhost;dbname=test', 'user', 'pass');
$stmt = $db->prepare('SELECT * FROM tags WHERE name=?');
$stmt->execute(array($_GET['tag']));
$db = new PDO('host=localhost;dbname=test', 'user', 'pass');
$stmt = $db->prepare('SELECT * FROM tags WHERE name=:tag');
$stmt->bindParam(':tag', $_GET['tag'], PDO::PARAM_STR);
$stmt->execute();

See the PDO documentation here.

PHP: Securing user input

This is the first of a series of posts about writing PHP in a safe and secure manner. So to start things off I will be talking about the proper use of user input and how to safetly sanitize it.

First things first and most importantly USER INPUT IS NOT TO BE TRUSTED.

User input can come in different forms, data inputted into a form, HTTP requests (GET, POST, PUT, DELETE) and cookie information. To get started here is an example of an insecure way of handling user input.

<?php
$db->query("SELECT * FROM users WHERE id=" . $_GET['id']);

The above example is a shocking and improper use of user input that will cause a SQL Injection vulnerability. A better safer method would be to make sure the id value from $_GET is an integer you can do this like so.

<?php
$db->query("SELECT * FROM users WHERE id=" . (int) $_GET['id']);

The above example is better but still not very secure I would say. PHP has an in built function that can be used to filter user input (filter_var). Here is an example using filter_var function.

<?php
$id = filter_var($_GET['id'], FILTER_SANITIZE_NUMBER_INT);
$db->query("SELECT * FROM users WHERE id=" . $id);

What I’m trying to say is all user input must be sanitized be it is used.

PHP has 4 global variables that are populated by user input:

  • $_GET
  • $_POST
  • $_FILES
  • $_REQUEST

There is also $_COOKIE this holds all the the HTTP Cookie info which can be changed or added to by the end user.

$_GET holds the values from a HTTP GET request.
$_POST holds the values from HTTP POST request.
$_FILES holds information about any files that have been uploaded.
$_REQUEST holds the vales from $_GET, $_POST & $_COOKIE ($_REQUEST should never be used as you cannot verify where that data actually come from use $_GET, $_POST & $_COOKIE never $_REQUEST)

See the PHP documentation for $_REQUEST here & OWASP here. It is consider bad practice to use $_REQUEST.

Anyway here is a function that you can use to sanitize user input easily: Github Gist

Tutorial: Silverstripe and Nginx

Today I will be talking you through Silverstripe running on Nginx via PHP-FPM with PostgreSQL on ubuntu.

First of all we will need Nginx.

$ sudo apt-get install nginx

Install PostgreSQL.

$ sudo apt-get install postgresql-9.1

Now we will need PHP and PHP-FPM.

$ sudo apt-get install php5 php5-cli php5-dev php5-pgsql php5-gd php5-curl php5-tidy

If your using PHP 5.5 you will need to install PHP5-JSON.

$ sudo apt-get install php5-json

Install composer. We will use composer to install Silverstripe.

$ curl -sS https://getcomposer.org/installer | php
$ sudo mv composer.phar /usr/local/bin/composer

Get Silverstripe code.

$ composer create-project silverstripe/installer /var/www/silverstripe/ 3.1.2

This will download the code for Silverstripe 3.1.2 into /var/www/silverstripe/
Now we must add the PostgreSQL plugin to the composer.json file in /var/www/silverstripe/

$ cd /var/www/silverstripe/
$ vim composer.json

And add the following line to the require section:

"silverstripe/postgresql": "dev-master"

Save the file and run the following command:

$ composer update

Now that we have all the code we need installed, now all that is left is the nginx config.

$ cd /etc/nginx/sites-available/
$ sudo vim silverstripe.conf

Here is an example nginx config: https://gist.github.com/chtombleson/8703899

Note: if you get any 502 bad gateway errors in the admin section you will need to add some extra settings, which are in the first comment in the above Nginx config example.

Now that we have the Nginx config setup we get need to enable it as follows:

$ sudo ln -s /etc/nginx/sites-available/silverstripe.conf /etc/nginx/sites-enabled/silverstripe.conf
$ sudo /etc/init.d/nginx restart

Now to create the database and database user.

$ sudo su - postgres
$ createuser -SDRP [database username]
$ createdb -O [database username] [database name]

Now go to your newly setup site http://example.com/install.php and run through the installer and once finished it will redirect you to your new Silverstripe site.

2014 A New Year of Code

It’s 2014 which can only mean one thing… More code, opensource projects and general awesomeness.

Starting this year off with building a node.js cms, Sonic CMS and Organisation Dashboard a PHP 5.4+ system to help organisations to manage their Facebook, Twitter and check analytics for their sites in one place.

 

Happy Coding and a late Happy New Year.

Common PHP mistakes in PHP 5.3+

I have written a lot of PHP over the past 6 years and time and time again I see little & big common mistakes.

1) Not sanitising user input before displaying it, executing it or putting it in the database.

2) Not using consistent naming convention s. (We have all done this before)

3) Not breaking big functions into smaller functions when possible. (Breaking up a big function can help with debugging and maintainability)

4) Not using classes and OOP.

5) Not using a database abstraction layer such as ABODB or even better yet PDO.

Not so much PHP related things

6) The belief anyone can write a secure site in PHP with little or a no experience. (This is an outright lie and is the same in any Programming language)

7) If I learn a PHP framework I have learned PHP. (You have learned how to use a tool or framework in this case, but you have not learnt all of what PHP has to offer. Or ruby if your doing rails)

This is more of a misconception with PHP.

8) PHP is insecure. (This can be said of any programming, damn Java. Its not the language that’s insecure but the way you used the language and the code you wrote.)

That rant is done. Hopefully there is something useful in there for you guys and gals.

Kiwi Pycon 2013

Pycon time yay :D squeeee

Testing Opensource Applications

To follow up on my last blog post about a DOS vulnerability in Silverstripe I thought I might talk about the importance of testing and show you a few handy tools that make it easy to test your applications.

So why testing important? Testing insurers that your application or code is running as you expect. Application/Code that is regularly tested give some confidence to your users or customer that if something does show up you will know about it and fix it. With that being said you can’t test for every use case, this is where unit tests come in.

Unit tests are used to test the Application/Code in small parts and breaks it down to functionality based testing for example, you could have a test that checked whether you could create, delete, edit, publish and un-publish a page, that would be a group of tests, you would have one for create, edit, delete, publish and un-publish. You can also setup the tests to depend on each other so if the create test fails the whole page test will fail. Having this single point of failure in tests can be handy.

Security testing is a whole different thing within itself. You can check for things like SQL injection and basic privilege escalation etc. But you can’t always find the less obvious security flaws. One solution if you can afford it is to hire a IT security company to pentest your Application/Code but this can be expensive. You could ask some friends to try and hack your application or just to have a look at the code, sometimes a second set of eyes is very helpful.

Here are a couple of cool free tools that can help you test your applications/code:

  • Travis CI – Travis CI is a hosted Continuous Integration tool, which can be integrated with Github and will run test when you push a new commit to Github.
  • Jenkins CI – Jenkins is the Continuous Integration tool that is used by Travis CI to run the tests, Jenkins can be self hosted which is a good idea for big projects and especially if you are doing automated deployments if the tests pass.

Here are some links on Unit Testing:

If your not testing your applications/code maybe you start now as it will help you avoid problems in the future.

Silverstripe 3 DOS vulnerable

Before I start I have a disclaimer: DISCLAIMER: “PLEASE NOTE I TAKE NO RESPONSIBILITY FOR OTHERS ACTIONS AND USE OF THE INFORMATION FOUND IN THIS BLOG POST, THIS VULNERABILITY IS ALREADY PUBLIC KNOWLEDGE !!!!!!!”

I have been doing some work recently with Silverstripe. Whilst doing some dev work I needed to flush the sites cache which is as easy as appending this ?flush=1 to the end of any url on the site. This got me thinking what would happen if someone sent a bunch of requests to different page on the site with ?flush=1 appended to the url.

So the next thing I did was set a production ready web server VM on my PC and install a traditional LAMP stack, Linux (Ubuntu 12.04), Apache, MySQL and PHP 5.4 (I would of also tested this using Nginx & PHP-FPM but the re-write rule for Nginx don’t work and couldn’t be bothered stuffing around). Then I downloaded and installed Silverstripe 3.0.5 on the server and got it up and running, I also didn’t bother to add any extra content to the default content given by a basic install.

So I did a basic test which was to find out how long a page takes to load when cached and when you flush the cache.

Results:

  • Cached Page: 153ms
  • Flushed Cache: 940ms

So as you can see the cached page load 8 to 9 times faster. This means there must be a lot happening in the background on the server side.

Next thing was to test to see if I could actually DOS the site, so I wrote a python script which used multiple processes to hit the site with urls that exist and append ?flush=1 to the url to flush the cache.

Basically the script spawns multiple processes that run the attack function which loops forever and hits a random url from a list and appends ?flush=1 to the url.

This script proved to be very effective and it worked as I thought it would.

Then I thought why hell am I able to do this… Other CMS’s such as drupal require you to have admin privileges to flush the cache but not Silverstripe. So this is not so much a DOS vulnerability but a security one also because you are giving a user escalated privileges although you can only really flush the cache.

The result of my DOS test were crazy. So when I started the server I had 6 Apache processes running, which is the default, I got up to about 30 processes then ssh died on the web server. So I looked at the VNC console and the system had pretty much crashed and was killing hundreds of Apache processes. And that was only with 50 processes hitting the site and it took about 5 minutes.

Next test was 100 processes which took about 2 minutes to kill the server. Then a 1000 processes which took less than a minute to kill the server.

So after I had my results I proceeded to email the security@silverstripe.org to report the bug as it’s as much a security bug as it is a DOS vulnerability. Turned out they have been handling this issue since the 28th of Febuary 2013 and what was more concerning was the fact it was a public ticket on the github bug tracker here.

I hope the NZ Government had Silverstripe pen-tested before the DIA (Department of Internal Affairs) flagged Silverstripe as the CMS for CWP (Common Web Platform).

I have blogged about this because not many people who are running Silverstripe 3 will know about this security bug and the potential threat it has to your website and server.

DISCLAIMER: “PLEASE NOTE I TAKE NO RESPONSIBILITY FOR OTHERS ACTIONS AND USE OF THE INFORMATION FOUND IN THIS BLOG POST, THIS VULNERABILITY IS ALREADY PUBLIC KNOWLEDGE !!!!!!!”

It took Silverstripe 5+ months but a week after my blog post a fix has been merged into master, https://github.com/silverstripe/silverstripe-framework/commit/1298d4a5bd927117f9893f32fd02a75ed10d623b. Good on them but it should of been treated seriously to begin with and had been fixed straight away.

Laters,
Christopher Tombleson

Cribz Router Tutorial

Today I will giving a tutorial on how to use Cribz Router.
Cribz Router is PHP 5 url routing library and is similar to the routing in Dancer.

Requirements:

  • A webserver that is configured to run php
  • A web config that rewrites all requests to index.php

First of all you can get Cribz Router from packagist, https://packagist.org/packages/cribz/router using composer.

First of all install composer:
curl -sS https://getcomposer.org/installer | php

Create a file called composer.json:

{
    "require": {
        "cribz/router": "v1.0.2"
    }
}

Run composer.phar install
This command reads the composer.json and downloads all the require libaries and put them into the vendor folder and creates a composer.lock file.

Here is a simple hello world using Cribz Router (index.php):

<?php
// Include the auto generated autoloader that is created via composer
require_once(__DIR__ . '/vendor/autoload.php');
use Cribz\RouterException;
use Cribz\Router;
try {
    Router::get('/', function($request, $params) {
        echo 'Hello world';
    });
    Router::run();
} catch (RouterException $e) {
    echo 'Error: ' . $e-&gt;getMessage();
}
?>;

Now if you hit the / in a browser it will show “Hello World”.

Now lets do a simple post route:

<?php
// Include the auto generated autoloader that is created via composer
require_once(__DIR__ . '/vendor/autoload.php');
use Cribz\RouterException;
use Cribz\Router;
try {
    // Handle HTTP GET request for /
    Router::get('/', function($request, $params) {
        echo 'Hello world';
    });
    // Handle HTTP GET request for /contact
    Router::get('/contact', function($request, $params) {
        echo '<form action="/contact" method="post"><textarea name="message"></textarea><input type="submit" /></form>';
    });
    // Handle HTTP POST request for /contact
    Router::post('/contact', function($request, $params) {
        // Get the user input from the params object
        $message = $params-&gt;post-&gt;message;
        echo 'Thank you for the message';
        echo 'Message: ' . $message;
    });
    Router::run();
} catch (RouterException $e) {
    echo 'Error: ' . $e-&gt;getMessage();
}
?>;

Now if you hit /contact you will be presented with a form, add a message then submit the form then it will present you with a thankyou message and the message you entered into the form.

Cribz Router is small but powerful library here is the full api:

Router::any(array $methods, string $uri, callback $function) Set a route for multiple HTTP request methods.

Router::delete(string $uri, callback $function) Set a route for a HTTP Delete request.

Router::exists(string $method, string $uri) Check if a route exists.

Router::get(string $uri, callback $function) Set a route for a HTTP Get request.

Router::head(string $uri, callback $function) Set a route for a HTTP Head request.

Router::options(string $uri, callback $function) Set a route for a HTTP Options request.

Router::post(string $uri, callback $function) Set a route for a HTTP Post request.

Router::put(string $uri, callback $function) Set a route for a HTTP Put request

Router::run() Run the routes. Wraps both runCli() & runHttp()

Router::runCli() Run routes from the Command Line.

Router::runHttp() Run routes from a HTTP request.

The libraries code is here: https://github.com/chtombleson/cribz-router

PHP 5 function callbacks

One of things I really like about javascript and particularly JQuery is use of callbacks to hook certain events, such as the success or failure of an ajax call.

This method of hooking events can be useful in php. For example you have a function that a connection to a database. By using callback function you can allow people to handle errors or modify the database
object on success.

Here is an example of what I mean:

<?php
function db_connect($dsn, $user, $pass, $success = null, $error = null) {
	try {
		$database = new PDO($dsn, $user, $pass);
		if (!is_null($success) && is_callable($success)) {
			return $success($database);
		} else {
			return $database;
		}
	} catch (PDOException $e) {
		if (!is_null($error) && is_callable($error)) {
			return $error($e->getMessage);
		} else {
			return false;
		}
	}
}
?>

All this function does is creates a new PDO Object. I have 2 callbacks in this function the first one is a success callback which is called if no exception is thrown when creating the new PDO Object. The second callback is a error callback which is called if an exception is thrown when creating the new PDO Object.

The success callback will take the newly created PDO Object as a parameter. You could use the success callback to set options for the new PDO Object.

Your success callback could look something like this.

<?php
function ($database) {
	$database->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
	return $database;
}
?>

The error callback will take the message from the exception as a parameter.

Your error callback could look something like this.

<?php
function ($errorMsg) {
	echo $errorMsg;
}
?>

Time to put it all together.

<?php
function db_connect($dsn, $user, $pass, $success = null, $error = null) {
	try {
		$database = new PDO($dsn, $user, $pass);
		if (!is_null($success) && is_callable($success)) {
			return $success($database);
		} else {
			return $database;
		}
	} catch (PDOException $e) {
		if (!is_null($error) && is_callable($error)) {
			return $error($e->getMessage);
		} else {
			return false;
		}
	}
}
$dsn = 'mysql:host=localhost;dbname=test;port=3306';
$db = db_connect($dsn, 'user', 'pass',
	function ($database) {
		$database->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
		return $database;
	},
	function ($errorMsg) {
		echo $errorMsg;
	}
);
?>

Using the above to defined your callbacks, it can be diffuclt to tell which callback is which. If we modify the db_connect function to take an array that contains our callback functions and modify the function call we get a more JQuery like syntax, like so.

<?php
function db_connect($dsn, $user, $pass, $callbacks = array()) {
	try {
		$database = new PDO($dsn, $user, $pass);
		if (isset($callbacks['success']) && is_callable($callbacks['success'])) {
			return $callbacks['success']($database);
		} else {
			return $database;
		}
	} catch (PDOException $e) {
		if (is_null($callbacks['error']) && is_callable($callbacks['error'])) {
			return $callbacks['error']($e->getMessage);
		} else {
			return false;
		}
	}
}
$dsn = 'mysql:host=localhost;dbname=test;port=3306';
$db = db_connect($dsn, 'user', 'pass', array(
	'success' => function ($database) {
		$database->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
		return $database;
	},
	'error' => function ($errorMsg) {
		echo $errorMsg;
	},
));
?>

Please note that this code has been tested on PHP 5.4 with that being said I’m not sure if the array with the callback functions will work in PHP 5.3 or lower.

Now you can use the power of callbacks and add some flare to your code.