Skip to content

Commit

Permalink
Merge branch 'enable-hsts'
Browse files Browse the repository at this point in the history
* enable-hsts:
  wording
  indentation
  wording
  more conditions
  more condition
  wording
  Enable/disable HTTP Strict Transport Security Header and set its value.
  set "Strict-Transport-Security header on httpsed page and it has strict_transport_security config
  Set The HTTP Strict Transport Security test
  Allow set HTTP Strict Transport Security Header value
  • Loading branch information
samsonasik committed Jan 22, 2017
2 parents c81f9c0 + 5d93d17 commit 4b2e7d0
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 10 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ ForceHttpsModule is a configurable module for force https in your ZF2/ZF3 Mvc Ap
Features
--------

- [x] enable/disable force https.
- [x] Enable/disable force https.
- [x] Force Https to All routes.
- [x] Force Https to specific routes only.
- [x] Keep headers, request method, and request body.
- [x] Enable/disable HTTP Strict Transport Security Header and set its value.

Installation
------------
Expand Down Expand Up @@ -55,6 +56,11 @@ return [
'checkout',
'payment'
],
// set HTTP Strict Transport Security Header
'strict_transport_security' => [
'enable' => true, // set to false to disable it
'value' => 'max-age=31536000',
],
],
];
```
Expand Down
5 changes: 5 additions & 0 deletions config/force-https-module.local.php.dist
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,10 @@ return [
// a lists of specific routes to be https
// only works if previous config 'force_all_routes' => false
],
// set HTTP Strict Transport Security Header
'strict_transport_security' => [
'enable' => true, // set to false to disable it
'value' => 'max-age=31536000',
],
],
];
103 changes: 103 additions & 0 deletions spec/Listener/ForceHttpsSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
]);

$eventManager = Double::instance(['implements' => EventManagerInterface::class]);
expect($eventManager)->toReceive('attach')->with(MvcEvent::EVENT_BOOTSTRAP, [$listener, 'setHttpStrictTransportSecurity']);
expect($eventManager)->toReceive('attach')->with(MvcEvent::EVENT_ROUTE, [$listener, 'forceHttpsScheme']);

$listener->attach($eventManager);
Expand All @@ -43,6 +44,7 @@
]);

$eventManager = Double::instance(['implements' => EventManagerInterface::class]);
expect($eventManager)->not->toReceive('attach')->with(MvcEvent::EVENT_BOOTSTRAP, [$listener, 'setHttpStrictTransportSecurity']);
expect($eventManager)->not->toReceive('attach')->with(MvcEvent::EVENT_ROUTE, [$listener, 'forceHttpsScheme']);

$listener->attach($eventManager);
Expand All @@ -57,6 +59,7 @@
]);

$eventManager = Double::instance(['implements' => EventManagerInterface::class]);
expect($eventManager)->not->toReceive('attach')->with(MvcEvent::EVENT_BOOTSTRAP, [$listener, 'setHttpStrictTransportSecurity']);
expect($eventManager)->not->toReceive('attach')->with(MvcEvent::EVENT_ROUTE, [$listener, 'forceHttpsScheme']);

$listener->attach($eventManager);
Expand All @@ -65,6 +68,106 @@

});

describe('->setHttpStrictTransportSecurity', function () {

it('add Strict Transport Security Header with max-age=0 (expire) if uri scheme is http and no "strict_transport_security" defined/empty', function () {

Console::overrideIsConsole(false);
$listener = new ForceHttps([
'enable' => true,
'force_all_routes' => true,
'force_specific_routes' => [],
]);

$mvcEvent = Double::instance(['extends' => MvcEvent::class, 'methods' => '__construct']);
$response = Double::instance(['extends' => Response::class]);

allow($mvcEvent)->toReceive('getRequest', 'getUri', 'getScheme')->andReturn('http');
allow($mvcEvent)->toReceive('getResponse')->andReturn($response);
allow($response)->toReceive('getHeaders', 'addHeaderLine')->with('Strict-Transport-Security: max-age=0');

expect($mvcEvent)->toReceive('getResponse');

$listener->setHttpStrictTransportSecurity($mvcEvent);

});

it('add Strict Transport Security Header with max-age=0 (expire) if uri scheme is https and no "strict_transport_security" defined/empty', function () {

Console::overrideIsConsole(false);
$listener = new ForceHttps([
'enable' => true,
'force_all_routes' => true,
'force_specific_routes' => [],
]);

$mvcEvent = Double::instance(['extends' => MvcEvent::class, 'methods' => '__construct']);
$response = Double::instance(['extends' => Response::class]);

allow($mvcEvent)->toReceive('getRequest', 'getUri', 'getScheme')->andReturn('https');
allow($mvcEvent)->toReceive('getResponse')->andReturn($response);
allow($response)->toReceive('getHeaders', 'addHeaderLine')->with('Strict-Transport-Security: max-age=0');

expect($mvcEvent)->toReceive('getResponse');

$listener->setHttpStrictTransportSecurity($mvcEvent);

});

it('add Strict Transport Security Header if uri scheme is http and "strict_transport_security" defined', function () {

Console::overrideIsConsole(false);
$listener = new ForceHttps([
'enable' => true,
'force_all_routes' => true,
'force_specific_routes' => [],
'strict_transport_security' => [
'enable' => true,
'value' => 'max-age=31536000',
],
]);

$mvcEvent = Double::instance(['extends' => MvcEvent::class, 'methods' => '__construct']);
$response = Double::instance(['extends' => Response::class]);

allow($mvcEvent)->toReceive('getRequest', 'getUri', 'getScheme')->andReturn('http');
allow($mvcEvent)->toReceive('getResponse')->andReturn($response);
allow($response)->toReceive('getHeaders', 'addHeaderLine')->with('Strict-Transport-Security: max-age=31536000');

expect($mvcEvent)->toReceive('getResponse');

$listener->setHttpStrictTransportSecurity($mvcEvent);

});

it('add Strict Transport Security Header if uri scheme is https and "strict_transport_security" defined', function () {

Console::overrideIsConsole(false);
$listener = new ForceHttps([
'enable' => true,
'force_all_routes' => true,
'force_specific_routes' => [],
'strict_transport_security' => [
'enable' => true,
'value' => 'max-age=31536000',
],
]);

$mvcEvent = Double::instance(['extends' => MvcEvent::class, 'methods' => '__construct']);
$response = Double::instance(['extends' => Response::class]);

allow($mvcEvent)->toReceive('getRequest', 'getUri', 'getScheme')->andReturn('https');
allow($mvcEvent)->toReceive('getResponse')->andReturn($response);
allow($response)->toReceive('getHeaders', 'addHeaderLine')->with('Strict-Transport-Security: max-age=31536000');

expect($mvcEvent)->toReceive('getResponse');

$listener->setHttpStrictTransportSecurity($mvcEvent);

});

});

describe('->forceHttpsScheme()', function () {

it('not redirect if uri already has https scheme', function () {
Expand Down
76 changes: 67 additions & 9 deletions src/Listener/ForceHttps.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,42 @@ public function attach(EventManagerInterface $events, $priority = 1)
return;
}

$this->listeners[] = $events->attach(MvcEvent::EVENT_BOOTSTRAP, [$this, 'setHttpStrictTransportSecurity']);
$this->listeners[] = $events->attach(MvcEvent::EVENT_ROUTE, [$this, 'forceHttpsScheme']);
}

/**
* Set The HTTP Strict Transport Security.
*
* @param MvcEvent $e
*/
public function setHttpStrictTransportSecurity(MvcEvent $e)
{
/** @var $request \Zend\Http\PhpEnvironment\Request */
$request = $e->getRequest();
$uriScheme = $request->getUri()->getScheme();

/** @var $response \Zend\Http\PhpEnvironment\Response */
$response = $e->getResponse();

if (
($this->isSchemeHttps($uriScheme) || $this->isGoingToBeForcedToHttps($e)) &&
isset(
$this->config['strict_transport_security']['enable'],
$this->config['strict_transport_security']['value']
) &&
$this->config['strict_transport_security']['enable'] === true
) {
$response->getHeaders()
->addHeaderLine('Strict-Transport-Security: ' . $this->config['strict_transport_security']['value']);
return;
}

// set max-age = 0 to strictly expire it,
$response->getHeaders()
->addHeaderLine('Strict-Transport-Security: max-age=0');
}

/**
* Force Https Scheme handle.
*
Expand All @@ -47,28 +80,53 @@ public function forceHttpsScheme(MvcEvent $e)
$request = $e->getRequest();
$uri = $request->getUri();
$uriScheme = $uri->getScheme();
if ($uriScheme === 'https') {
return;
}

if (! $this->config['force_all_routes'] &&
! in_array(
$e->getRouteMatch()->getMatchedRouteName(),
$this->config['force_specific_routes']
)
) {
if ($this->isSchemeHttps($uriScheme) || ! $this->isGoingToBeForcedToHttps($e)) {
return;
}

/** @var $response \Zend\Http\PhpEnvironment\Response */
$response = $e->getResponse();
$httpsRequestUri = $uri->setScheme('https')->toString();

// 307 keeps headers, request method, and request body
$response->setStatusCode(307);
$response->getHeaders()
->addHeaderLine('Location', $httpsRequestUri);
$response->send();

exit(0);
}

/**
* Is Scheme https ?
*
* @param string $uriScheme
*
* @return bool
*/
private function isSchemeHttps($uriScheme)
{
return $uriScheme === 'https';
}

/**
* Check Config if is going to be forced to https.
*
* @param MvcEvent $e
* @return bool
*/
private function isGoingToBeForcedToHttps(MvcEvent $e)
{
if (! $this->config['force_all_routes'] &&
! in_array(
$e->getRouteMatch()->getMatchedRouteName(),
$this->config['force_specific_routes']
)
) {
return false;
}

return true;
}
}

0 comments on commit 4b2e7d0

Please sign in to comment.