HTTPS SSH

NetglueSSL ZF2 Module

The purpose of this module is to perform redirects either manually from within controllers or by specifying controllers/routes/urls that must be ssl or non-ssl within config.

Installation

Installation should be done with composer by adding to your composer.json the following:

require {
    'netglue/zf2-ssl-module': "dev-master"
}

Depending whether the module has been tagged with any release or stability information, you may also have to add to composer the "minimum-stability":"dev" setting.

Once you've run $ php composer.phar update, add 'NetglueSSL' as a module name to your ZF2 applications config/application.config.php file, copy the dist config found in vendor/netglue/zf2-ssl-module/config/module.config.php.dist to your autoloading configuration directory and rename/modify to suit your needs.

Contributing, Feedback & Issues

Module Home Page

This module has been made primarily for my own use as a quick and dirty solution to a common problem I face. If it's useful to you I'd love to know about it and if you have improvements to contribute, great, go forth and fork. I don't have the time to answer support requests but if you find any bugs, feel free to add them to the issue tracker

Todo

  • Tests

Controller Plugins

There are 2 controller plugins: forceSSL and forceHttp. Providing the module is correctly configured, within any controller action, you can issue a $this->forceSSL() to redirect to the SSL host if currently connected over standard http and the reverse for $this->forceHttp()

To stop the rest of the action running before the redirect occurs, you should wrap the calls to the controller plugins with something like this:

if($this->forceSSL()) {
    return;
}

The __invoke() methods of both forceSSL and forceHttp will return either a Zend\Http\Response object if a redirect is required, or NULL if the current request is already using the required protocol.

Configuration

The main options tells the module about the SSL and non-SSL hosts you're using. If the app is running on a server where both of these hosts are the same, both HTTP and HTTPS are on the standard ports and there's no differing prefix to uris for each host, all the options can be left unset, or null.

Supposing that these two uris are equivalent:

http://www.example.com/app-root/some-action
https://www.ssl-host.com/secure/app-root/some-action

Your configuration of the basic options would be:

'ssl_hostname' => 'www.ssl-host.com',
'http_hostname' => 'www.example.com',
'ssl_path_prefix' => '/secure',
'http_path_prefix' => NULL,

Forcing SSL/Non-SSL with Route Parameters

To force any of your configured routes to use SSL, add 'force-ssl' => 'ssl' to the route defaults or 'force-ssl' => 'http' to force HTTP.

Using this approach allows you to set a default for a route and override it in child routes. Rather than 'forcing' it, you can also set force-ssl to false to in a child route to stop any further evaluation taking place.

This method is first in evaluation and circumvents testing of the uri using controller, route name and uri configuration

Forcing SSL/Non-SSL with Controllers, route names and partial uris

The other options ssl_only and http_only are expected to take the form of the following array

'ssl_only' => array(
    'controllers' => array(
        'Some\Controller\Class', // As it would be when retreived from the RouteMatch object, i.e. 'Application\Controller\Index'
    ),
    'routes' => array(
        'home',
        'some/other/routename',
    ),
    'uris' => array(
        '/account',
        '/login',
    ),
);

The matching for these is really basic and you'll have to be careful not to create endless redirection loops.

Any controller name in the 'controllers' array will be matched if in_array($controllerName, $searchArray) evaluates to true. So, if you were to add the same controller to both ssl_only and http_only you'd be straight into an endless redirection loop.

The same is true of route names.

The uri array is slightly different. Each element of this array is transformed into a basic regex, so a value of /ssl-only-please will be turned into the pattern /^\/ssl-only-please/

Only the path part of the uri is tested, so query strings, hostname, fragments etc are ignored. You should expect the example to match urls such as /ssl-only-please/foo, /ssl-only-please?blah=blah

Deciding whether to redirect is done in the following order, controllers, routes, uris. As soon as a match is made, the response is returned and the redirect occurs so you can't force ssl for an entire controller and force std http for one of it's actions. it's just not that clever. You'd be better off using route parameters if that's waht you're after

Caveats

As previously mentioned, controller, route name and uri pattern matching is a country mile from intelligent and could be greatly improved I'm sure. Also, if you end up using any of the configuration options, including setting route params, it's probably best to avoid using the controller plugins because you might end up in infinite redirect loops with those. For example if you define a route such as 'my-route' with forced SSL as a route param and then in the corresponding controller, issue a $this->forceHttp(), clearly, you'll be going around in circles!