enygma / shieldframework

Shield - A Security Minded Microframework
142 stars 15 forks source link

Shield : A Security-Minded Microframework

Build Status

In my efforts to learn more about security best practices in PHP, I noticed that most of the PHP frameworks out there left it up to the developer to correctly handle input/output/etc themselves. Unfortunately, this has been a sticking point in PHP apps, so I decided to work on a microframework that was designed with security in mind.

This project is under a MIT license.

shieldframework.com

Disclaimer

Please note: This framework is a work in progress and is serving as a resource to learn more about PHP and web application security. Use of this framework will not provide the perfect security for your application, nor should it be considered an ultimate resource for security best practices.

Features:

Requires

The Code

I'm a big fan of the Slim microframework, so anyone that's used that will feel at home with Shield. Here's some example code of it in use:

<?php
include_once '../Shield/Shield.php';
$app = new Shield\Shield();

$app->get('/',function(){
    echo 'website root! woo!';
});

$app->run();

The above example is super simple - all it does is handle (thanks to the included .htaccess file) the request for the root level route "/" as a GET request. When it matches the route, it executes the closure callback and echos out "website root! woo!" Easy right?

Let's take a look at something a bit more complicated to introduce you to a few other handy tools at your disposal:

<?php
include_once '../Shield/Shield.php';
$app = new Shield\Shield();

$app->get('/',function() use ($app){

    $app->filter->add('test','email');

    echo 'from the URL: '.$app->input->get('test').'<br/>';

    $app->view->set('test','<a href="https://github.com/enygma/shieldframework/blob/master/">foodles</a>');
    return $app->view->render('index1 [test]');
});

$app->run();

First off, there's one key difference between this example and the first one. In this example we pass in the $app object itself so we have access to some special features. Here's a quick overview:

There's also one other thing that could help in more complex development - the DI container. The framework makes heavy use of a Dependency Injection Container (DIC) to work with its resources. This is exposed back to the user as well, so you can access $app->di and use it to manage your own object instances as well.

Regular Expression Routing

Besides the ability for Shield to match exact routes (like /foo), there's also a feature included allowing you use regular expresions in your routing. For example:

<?php
include_once '../Shield/Shield.php';
$app = new Shield\Shield();

$app->get('/foo([0-9]+)', function($matches) {
    print_r($matches);
});

Shield will try to match exact routes first, but then fall back on the regex routing checks. In th eabove example we're matching a route like /foo123. You'll notice that the first argument for the method is the routing matches as pulled from the preg_match PHP method. You can use whatever preg-based expression you want to use and have the values returned to you in the $matches value. So:

<?php
include_once '../Shield/Shield.php';
$app = new Shield\Shield();

$app->get('/foo([0-9]+)', function($matches){
    print_r($matches);
});

You would get Array ( [1] => 123 ) in the $matches variable.

You can also use named parameters in your routes too:

include_once '../Shield/Shield.php';
$app = new Shield\Shield();

$app->get('/params/[:t1]/[:t2]', function($matches){
    return $app->view->render('First Parameter: '.$matches['t1']);
});

If your route is /params/123/456 you'll get:

Array ( [t1] => 123, [t2] => 456 ) in the $matches variable.

NOTE: DO NOT directly use the values from this array - there is currently no filtering on these values so there is potential for exploitation.

Bound Configuration

You can also specify some configuration options linked directly to the route/closure combination. Here's an example:

<?php
include_once '../Shield/Shield.php';
$app = new Shield\Shield();

$app->get('/xml', function() use ($app){
    return $app->view->render('<test>this is xml</test>');
}, array(
    'view.content-type' => 'text/xml'
));

In the above example, we're overriding the view.content-type setting, but only for the / route, not everything. This gives us a bit more control over the application, making it easier to customize the request handling. Note this uses the dot notation to specify the value (the key). Most configuration options should be available for reconfiguration via this method.

Documentation

Shield

The Shield class is the main class you'll use and really only has a handful of methods:

Config

Access the values loaded from the configuration file or set/read your own.

Di

Access to the dependency injection container (getting & setting)

Filter

Filter values based on filter types (supported are: email, striptags). Filters are applied when get() is called.

NOTE: If no filters are specified, it will execute a "strip_tags" on the data by default.

The $type parameter for the add() method can either be a string for the filter type or it can be a \Closure that will be given the value of the field as a parameter - for example:

<?php

$app->filter->add('myField', function($value) {
    return 'returned: '.$value;
});

You must be sure to return from this closure, otherwise the filtering will return null.

Input

Pull values from the PHP superglobals (filtered)

NOTE: Superglobals are unset following a creation of an Input object.

Log

Logging to a file

View

Handle output to the page

NOTE: All values are escaped/filtered by default to prevent XSS. This can be overridden if desired.

Template

A basic templating engine included in the framework. By default it looks for a file named with the string given (in views/) or falls back to a str_replace method treating it as a string.

NOTE: If you choose to use the string as a template (no file), you must use the "[varName]" notation to get the values to substitute. Values can be set directly to the template instance (ex. $app->view->template->test = 'foo';)

Configuration

An optional config.php file can be placed in the same root as your front controller (probably index.php) so it can be found by the framework. This configuration file is a PHP array returned with your settings. These values can be accessed through the $di->get('Config')->get() method call. Here's an example config:

<?php
return array(
    'log_path' => '/tmp'
);

Additionally, you can use a "dotted notation" to find configuration options. So, for example, to find the value below:

<?php
return array(
    'foo' => array(
        'bar' => array(
            'baz' => 'testing this'
        )
    )
);

You can use $app->config->get('foo.bar.baz'); to get the value "testing this".

Available Config options

How To Contribute

First off, thanks for considering submitting changes for the project - help is always appreciated! If you're going to contribute to the project, here's a few simple steps to follow:

Shield and the OWASP "Top Ten"

One of the "gold standards" in the web application security community is the infamous "Top Ten" list of common security issues that web apps have. Shield, being the nice framework that it is, tries to help protect you and your app from these problems. Here's how:

Contact

Chris Cornutt ccornutt@phpdeveloper.org

@enygma