In our previous two posts, we covered zend-filter and zend-validator. With these two components, you now have the tools necessary to ensure any given user input is valid, fulfilling the first half of the "filter input, escape output" mantra.

However, as we discussed in the zend-validator article, as powerful as validation chains are, they only allow you to validate a single value at a time. How do you go about validating sets of values — such as data submitted from a form, or a resource for an API?

To solve that problem, Zend Framework provides zend-inputfilter. An input filter aggregates one or more inputs, any one of which may also be another input filter, allowing you to validate complex, multi-set, and nested set values.

Installation

To install zend-inputfilter, use Composer:

$ composer require zendframework/zend-inputfilter

zend-inputfilter only directly requires zend-filter, zend-stdlib, and zend-validator. To use its powerful factory feature, however, you'll also need zend-servicemanager, as it greatly simplifies creation of input filters:

$ composer require zendframework/zend-servicemanager

Theory of operation

An input filter composes one or more inputs, any of which may also be an input filter (and thus represent a set of data values).

Any given input is considered required by default, but can be configured to be optional. When required, an input will be considered invalid if the value is not present in the data set, or is empty. When optional, if the value is not present, or is empty, it is considered valid. An additional flag, allow_empty, can be used to allow empty values for required elements; still another flag, continue_if_empty, will force validation to occur for either required or optional values if the value is present but empty.

When validating a value, two steps occur:

  • The value is passed to a filter chain in order to normalize the value. Typical normalizations include stripping non-digit characters for phone numbers and credit card numbers; trimming whitespace; etc.
  • The value is then passed to a validator chain to determine if the normalized value is valid.

An input filter aggregates the inputs, as well as the values themselves. You pass the user input to the input filter after it has been configured, and then check to see if it is valid. If it is, you can pull the normalized values from it (as well as the raw values, if desired). If any value is invalid, you would then pull the validation error messages from it.

Stateless operation

The current approach is stateful: values are passed to the input filter before you execute its isValid() method, and then the values and any validation error messages are stored within the input filter instance for later retrieval. This can cause issues if you wish to use the same input filter multiple times.

For this reason, the planned version 3 release will be stateless: calling isValid() will require passing the value(s) to validate, and both inputs and input filters alike will return a result object from this method with the raw and normalized values, the result of validation, and any validation error messages.

Getting started

Let's consider a registration form where we want to capture a user email and their password. In our first example, we will use explicit usage, which does not require the use of plugin managers.

use Zend\Filter;
use Zend\InputFilter\Input;
use Zend\InputFilter\InputFilter;
use Zend\Validator;

$email = new Input('email');
$email->getFilterChain()
		->attach(new Filter\StringTrim());
$email->getValidatorChain()
		->attach(new Validator\EmailAddress());

$password = new Input('password');
$password->getValidatorChain()
		->attach(new Validator\StringLength(8), true)
	  ->attach(new Validator\Regex('/[a-z]/'))
	  ->attach(new Validator\Regex('/[A-Z]/'))
	  ->attach(new Validator\Regex('/[0-9]/'))
	  ->attach(new Validator\Regex('/[.!@#$%^&*;:]/'));

$inputFilter = new InputFilter();
$inputFilter->add($email);
$inputFilter->add($password);
$inputFilter->setData($_POST);

if ($inputFilter->isValid()) {
    echo "The form is valid\n";
    $values = $inputFilter->getValues();
} else {
    echo "The form is not valid\n";
    foreach ($inputFilter->getInvalidInput() as $error) {
        var_dump($error->getMessages());
    }
}

The above creates two inputs, one each for the incoming email address and password. The email address will be trimmed of whitespace, and then validated. The password will be validated only, checking that we have a value of at least 8 characters, with at least one each of lowercase, uppercase, digit, and special characters. Further, if any given character is missing, we'll get a validation error message so that the user knows how to create their password.

Each input is added to an input filter instance. We pass the form data (via the $_POST superglobal), and then check to see if it is valid. If so, we grab the values from it (we can get the original values via getRawValues()). If not, we grab error messages from it.

By default, all inputs are considered required. Let's say we also wanted to collect the user's full name, but make that optional. We could create an input like the following:

$name = new Input('user_name');
$name->setRequired(false);             // OPTIONAL!
$name>getFilterChain()
		->attach(new Filter\StringTrim());

Input specifications

As noted in the "Installation" section, we can leverage zend-servicemanager and the various plugin managers composed in it in order to create our filters.

Zend\InputFilter\InputFilter internally composes Zend\InputFilter\Factory, which itself composes:

  • Zend\InputFilter\InputFilterPluginManager, a plugin manager for managing Zend\InputFilter\Input and Zend\InputFilter\InputFilter instances.
  • Zend\Filter\FilterPluginManager, a plugin manager for filters.
  • Zend\Validator\ValidatorPluginManager, a plugin manager for validators.

The upshot is that we can often use specifications instead of instances to create our inputs and input filters.

As such, our above examples can be written like this:

use Zend\InputFilter\InputFilter;

$inputFilter = new InputFilter();
$inputFilter->add([
    'name' => 'email',
    'filters' => [
        ['name' => 'StringTrim']
    ],
    'validators' => [
        ['name' => 'EmailAddress']
    ],
]);
$inputFilter->add([
    'name' => 'user_name',
    'required' => false,
    'filters' => [
        ['name' => 'StringTrim']
    ],
]);
$inputFilter->add([
    'name' => 'password',
    'validators' => [
        [
            'name' => 'StringLength',
            'options' => ['min' => 8],
            'break_chain_on_failure' => true,
        ],
        ['name' => 'Regex', 'options' => ['pattern' => '/[a-z]/'],
        ['name' => 'Regex', 'options' => ['pattern' => '/[A-Z]/'],
        ['name' => 'Regex', 'options' => ['pattern' => '/[0-9]/'],
        ['name' => 'Regex', 'options' => ['pattern' => '/[.!@#$%^&*;:]/'],
    ],
]);

There are a number of other fields you could use:

  • type allows you to specify the input or input filter class to use when creating the input.
  • error_message allows you to specify a single error message to return for an input on validation failure. This is often useful as otherwise you'll get an array of messages for each input.
  • allow_empty and continue_if_empty, which were discussed earlier, and control how validation occurs when empty values are encountered.

Why would you do this instead of using the programmatic interface, though?

First, this approach leverages the various plugin managers, which means that any given input, input filter, filter, or validator will be pulled from their respective plugin manager. This allows you to provide additional types easily, but, more importantly, override existing types.

Second, the configuration-based approach allows you to store the definitions in configuration, and potentially even override the definitions via configuration merging! Apigility utilizes this feature heavily.

Managing the plugin managers

To ensure that you can use already configured plugin managers, you can inject them into the Zend\InputFilter\Factory composed in your input filter. As an example, considering the following service factory for an input filter:

function (ContainerInterface $container)
{
    $filters = $container->get('FilterManager');
    $validators = $container->get('ValidatorManager');
    $inputFilters = $container->get('InputFilterManager');

    $inputFilter = new InputFilter();
    $inputFilterFactory = $inputFilter->getFactory();
    $inputFilterFactory->setDefaultFilterChain($filters);
    $inputFilterFactory->setDefaultValidatorChain($validators);
    $inputFilterFactory->setInputFilterManager($inputFilters);

    // add inputs to the $inputFilter, and finally return it...
    return $inputFilter;
}

Managing Input Filters

The InputFilterPluginManager allows you to define input filters with dependencies, which gives you the ability to create re-usable, complex input filters. One key aspect to using this feature is that the InputFilterPluginManager also ensures the configured filter and validator plugin managers are injected in the factory used by the input filter, ensuring any overrides or custom filters and validators you've defined are present.

To make this work, the base InputFilter implementation also implements Zend\Stdlib\InitializableInterface, which defines an init() method; the InputFilterPluginManager calls this after instantiating your input filter and injecting it with a factory composing all the various plugin manager services.

What this means is that if you use this method to add() your inputs and nested input filters, everything will be properly configured!

As an example, let's say we have a "transaction_id" field, and that we need to check if that transaction identifier exists in the database. As such, we may have a custom validator that depends on a database connection to do this. We could write our input filter as follows:

namespace MyBusiness;

use Zend\InputFilter\InputFilter;

class OrderInputFilter extends InputFilter
{
    public function init()
    {
        $this->add([
            'name' => 'transaction_id',
            'validators' => [
                ['name' => TransactionIdValidator::class],
            ],
        ]);
    }
}

We would then register this in our input_filters configuration:

// in config/autoload/input_filters.global.php
return [
    'input_filters' => [
        'invokables' => [
            MyBusiness\OrderInputFilter::class => MyBusiness\OrderInputFilter::class,
        ],
    ],
    'validators' => [
        'factories' => [
            MyBusiness\TransactionIdValidator::class => MyBusiness\TransactionIdValidatorFactory::class,
        ],
    ],
];

This approach works best with the specification form; otherwise you need to pull the various plugin managers from the composed factory and pass them to the individual inputs:

$transId = new Input();
$transId->getValidatorChain()
    ->setValidatorManager($this->getFactory()->getValidatorManager());
$transId->getValidatorChain()
    ->attach(TransactionIdValidator::class);

Specification-driven input filters

Finally, we can look at specification-driven input filters.

The component provides an InputFilterAbstractServiceFactory. When you request an input filter or input that is not directly in the InputFilterPluginManager, this abstract factory will then check to see if a corresponding value is present in the input_filter_specs configuration array. If so, it will pass that specification to a Zend\InputFilter\Factory configured with the various plugin managers in order to create the instance.

Using our original example, we could define the registration form input filter as follows:

return [
    'input_filter_specs' => [
        'registration_form' => [
            [
                'name' => 'email',
                'filters' => [
                    ['name' => 'StringTrim']
                ],
                'validators' => [
                    ['name' => 'EmailAddress']
                ],
            ],
            [
                'name' => 'user_name',
                'required' => false,
                'filters' => [
                    ['name' => 'StringTrim']
                ],
            ],
            [
                'name' => 'password',
                'validators' => [
                    [
                        'name' => 'StringLength',
                        'options' => ['min' => 8],
                        'break_chain_on_failure' => true,
                    ],
                    ['name' => 'Regex', 'options' => ['pattern' => '/[a-z]/'],
                    ['name' => 'Regex', 'options' => ['pattern' => '/[A-Z]/'],
                    ['name' => 'Regex', 'options' => ['pattern' => '/[0-9]/'],
                    ['name' => 'Regex', 'options' => ['pattern' => '/[.!@#$%^&*;:]/'],
                ],
            ],
        ],
    ],
];

We would then retrieve it from the input filter plugin manager:

$inputFilter = $inputFilters->get('registration_form');

Considering most input filters do not need to compose dependencies other than the inputs and input filters they aggregate, this approach makes for a dynamic way to define input validation.

Topics not covered

zend-inputfilter has a ton of other features as well:

  • Input and input filter merging.
  • Handling of array values.
  • Collections (repeated data sets of the same structure).
  • Filtering of file uploads.

On top of all this, it provides a number of interfaces against which you can program in order to write completely custom functionality!

One huge strength of zend-inputfilter is that it can be used for any sort of data set you need to validate: forms, obviously, but also API payloads, data retrieved from a message queue, and more. Let us know what you use it for!

Save the date!

Want to learn more about Expressive and Zend Framework? What better location than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas, Nevada, USA. Visit the ZendCon website for more information.

Source

In our previous post, we covered zend-filter, The filters in zend-filter are generally used to pre-filter or normalize incoming data. This is all well and good, but we still don't know if the data is valid. That's where zend-validator comes in.

Installation

To install zend-validator, use Composer:

$ composer require zendframework/zend-validator

Like zend-filter, the only required dependency is zend-stdlib. However, a few other components are suggested, based on which filters and/or features you may want to use:

  • zendframework/zend-servicemanager is used by the ValidatorPluginManager and ValidatorChain to look up validators by their short name (versus fully qualified class name), as well as to allow usage of validators with dependencies.
  • zendframework/zend-db is used by a pair of validators that can check if a matching record exists (or does not!).
  • zendframework/zend-uri is used by the Uri validator.
  • The CSRF validator requires both zendframework/zend-math and zendframework/zend-session.
  • zendframework/zend-i18n and zendframework/zend-i18n-resources can be installed in order to provide translation of validation error messages.

For our examples, we'll be using the ValidatorChain functionality with a ValidatorPluginManager, so we will also want to install zend-servicemanager:

$ composer require zendframework/zend-servicemanager

ValidatorInterface

The current incarnation of zend-validator is stateful; validation error messages are stored in the validator itself. As such, validators must implement the ValidatorInterface:

namespace Zend\Validator;

interface ValidatorInterface
{
    /**
     * @param mixed $value
     * @return bool
     */
    public function isValid($value);

    /**
     * @return array
     */
    public function getMessages();
}

The $value can be literally anything; a validator examines it to see if it is valid, and returns a boolean result. If it is invalid, a subsequent call to getMessages() should return an associative array with the keys being message identifiers, and the values the human-readable message strings.

As such, usage looks like the following:

if (! $validator->isValid($value)) {
    // Invalid value
    echo "Failed validation:\n";
    foreach ($validator->getMessages() as $message) {
        printf("- %s\n", $message);
    }
    return false;
}
// Valid value!
return true;

Stateless validations are planned

We are planning to rewrite the zend-validator component for its version 3 release to be stateless. When we do, the ValidatorInterface will be rewritten to have isValid() return a ValidationResult. That instance will provide a method for determining if the validation was successful, encapsulate the value that was validated, and, for invalid values, provide access to the validation error messages. Doing so will allow better re-use of validators within the same execution process.

zend-validator provides a few dozen filters for common operations, including things like:

  • Common conditionals like LessThan, GreaterThan, Identical, NotEmpty, IsInstanceOf, InArray, and Between.
  • String values, such as StringLength, Regex.
  • Network-related values such as Hostname, Ip, Uri, and EmailAddress.
  • Business values such as Barcode, CreditCard, GpsPoint, Iban, and Uuid.
  • Date and time related values such as Date, DateStep, and Timezone.

Any of these validators may be used by themselves.

In many cases, though, your validation may be related to a set of validations: as an example, the value must be non-empty, a certain number of characters, and fulfill a regular expression. Like filters, zend-validator allows you to do this with chains.

ValidatorChain

Usage of a validator chain is similar to filter chains: attach validators you want to execute, and then pass the value to the chain:

use Zend\Validator;

$validator = new Validator\ValidatorChain();
$validator->attach(new Validator\NotEmpty());
$validator->attach(new Validator\StringLength(['min' => 6]));
$validator->attach(new Validator\Regex('/^[a-f0-9]{6,12}$/');

if (! $validator->isValid($value)) {
    // Failed validation
    var_dump($validator->getMessages());
}

The above uses validator instances, eliminating the need for ValidatorPluginManager, and thus avoids usage of zend-servicemanager. However, if we have zend-servicemanager installed, we can replace usage of attach() with attachByName():

use Zend\Validator;

$validator = new Validator\ValidatorChain();
$validator->attachByName('NotEmpty');
$validator->attachByName('StringLength', ['min' => 6]);
$validator->attachByName('Regex', ['pattern' => '/^[a-f0-9]{6,12}$/']);

if (! $validator->isValid($value)) {
    // Failed validation
    var_dump($validator->getMessages());
}

Breaking the chain

If you were to run either of these examples with $value = '', you may discover something unexpected: you'll get validation error messages for every single validator! This seems wasteful; there's no need to run the StringLength or Regex validators if the value is empty, is there?

To solve this problem, when attaching a validator, we can tell the chain to break execution if the given validator fails. This is done by passing a boolean flag:

  • as the second argument to attach()
  • as the third argument to attachByName() (the second argument is an array of constructor options)

Let's update the second example:

use Zend\Validator;

$validator = new Validator\ValidatorChain();
$validator->attachByName('NotEmpty', [], true);
$validator->attachByName('StringLength', ['min' => 6], true);
$validator->attachByName('Regex', ['pattern' => '/^[a-f0-9]{6,12}$/']);

if (! $validator->isValid($value)) {
    // Failed validation
    var_dump($validator->getMessages());
}

The above adds a boolean true as the $breakChainOnFailure argument to the attachByName() method calls of the NotEmpty and StringLength validators (we had to provide an empty array of options for the NotEmpty validator so we could pass the flag). In these cases, if the value fails validation, no further validators will be executed.

Thus:

  • $value = '' will result in a single validation failure message, produced by the NotEmpty validator.
  • $value = 'test' will result in a single validation failure message, produced by the StringLength validator.
  • $value = 'testthis' will result in a single validation failure message, produced by the Regex validator.

Prioritization

Validators are executed in the same order in which they are attached to the chain by default. However, internally, they are stored in a PriorityQueue; this allows you to provide a specific order in which to execute the validators. Higher values execute earlier, while lower values (including negative values) execute last. The default priority is 1.

Priority values may be passed as the third argument to attach() and fourth argument to attachByName().

As an example:

$validator = new Validator\ValidatorChain();
$validator->attachByName('StringLength', ['min' => 6], true, 1);
$validator->attachByName('Regex', ['pattern' => '/^[a-f0-9]{6,12}$/'], false, -100);
$validator->attachByName('NotEmpty', [], true, 100);

In the above, when executing the validation chain, the order will still be NotEmpty, followed by StringLength, followed by Regex.

Why prioritize?

Why would you use this feature? The main reason is if you want to define validation chains via configuration, and cannot guarantee the order in which the items will be present in configuration. By adding a priority value, you can ensure that recreation of the validation chain will preserve the expected order.

Context

Sometimes we may want to vary how we validate a value based on whether or not another piece of data is present, or based on that other piece of data's value. zend-validator offers an unofficial API for that, via an optional $context value you can pass to isValid(). The ValidatorChain accepts this value, and, if present, will pass it to each validator it composes.

As an example, let's say you want to capture an email address (form field "contact"), but only if the user has selected a radio button allowing you to do so (form field "allow_contact"). We might write that validator as follows:

use ArrayAccess;
use ArrayObject;
use Zend\Validator\EmailAddress;
use Zend\Validator\ValidatorInterface;

class ContactEmailValidator implements ValidatorInterface
{
    const ERROR_INVALID_EMAIL = 'contact-email-invalid';

    /** @var string */
    private $contextVariable;

    /** @var EmailAddress */
    private $emailValidator;

    /** @var string[] */
    private $messages = [];

    /** @var string[] */
    private $messageTemplates = [
        self::ERROR_INVALID_EMAIL => 'Email address "%s" is invalid',
    ];

    public function __construct(
        EmailAddress $emailValidator = null,
        string $contextVariable = 'allow_contact'
    ) {
        $this->emailValidator = $emailValidator ?: new EmailAddress();
        $this->contextVariable = $contextVariable;
    }

    public function isValid($value, $context = null)
    {
        $this->messages = [];

        if (! $this->allowsContact($context)) {
            // Value will be discarded, so always valid.
            return true;
        }

        if ($this->emailValidator->isValid($value)) {
            return true;
        }

        $this->messages[self::ERROR_INVALID_EMAIL] = sprintf(
            $this->messageTemplates[self::ERROR_INVALID_EMAIL],
            var_export($value, true)
        );
        return false;
    }

    public function getMessages()
    {
        return $this->messages;
    }

    private function allowsContact($context) : bool
    {
        if (! $context ||
            ! (is_array($context)
              || $context instanceof ArrayObject
              || $context instanceof ArrayAccess)
        ) {
            return false;
        }

        $allowsContact = $context[$this->contextVariable] ?? false;

        return (bool) $allowsContact;
    }
}

We would then add it to the validator chain, and call it like so:

$validator->attach(new ContactEmailValidator());
if (! $validator->isValid($data['contact'], $data)) {
    // Failed validation!
}

This approach can allow for some quite complex validation routines, particularly if you nest validation chains within custom validators!

Registering your own validators.

If you write your own validators, chances are you'll want to use them with the ValidatorChain. This class composes a ValidatorPluginManager, which is a plugin manager built on top of zend-servicemanager. As such, you can register your validators with it:

$plugins = $validator->getPluginManager();
$plugins->setFactory(ContactEmailValidator::class, ContactEmailValidatorFactory::class);
$plugins->setService(ContactEmailValidator::class, $contactEmailValidator);

Alternately, if using zend-mvc or Expressive, you can provide configuration via the validators configuration key:

return [
    'validators' => [
        'factories' => [
            ContactEmailValidator::class => ContactEmailValidatorFactory::class,
        ],
    ],
];

If you want to use a "short name" to identify your validator, we recommend using an alias, aliasing the short name to the fully qualified class name.

Wrapping up

Between using zend-filter to normalize and pre-filter values, and zend-validator to validate the values, you can start locking down the input your users submit to your application.

That said, what we've demonstrated so far is how to work with single values. Most forms submit sets of values; using the approaches so far can lead to a lot of code!

We have a solution for this as well, via our zend-inputfilter component, which we'll be detailing in our next post!

Save the date!

Want to learn more about Expressive and Zend Framework? What better location than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas, Nevada, USA. Visit the ZendCon website for more information.

Source

When securing your website, the mantra is "Filter input, escape output." We previously covered escaping output with our post on zend-escaper. We're now going to turn to filtering input.

Filtering input is rather complex, and spans a number of practices:

  • Filtering/normalizing input. As an example, your web page may have a form that allows submitting a credit card number. These have a variety of formats that may include spaces or dashes or dots — but the only characters that are of importance are the digits. As such, you will want to normalize such input to strip out the unwanted characters.
  • Validating input. Once you have done such normalization, you can then check to see that the data is actually valid for its context. This may include one or more rules. Using our credit card example, you might first check it is of an appropriate length, and then verify that it begins with a known vendor digit, and only after those pass, validate the number against a online service.

For now, we're going to look at the first item, filtering and normalizing input, using the component zend-filter.

Installation

To install zend-filter, use Composer:

$ composer require zendframework/zend-filter

Currently, the only required dependency is zend-stdlib. However, a few other components are suggested, based on which filters and/or featurse you may want to use:

  • zendframework/zend-servicemanager is used by the FilterChain component for looking up filters by their short name (versus fully qualified class name).
  • zendframework/zend-crypt is used by the encryption and decryption filters.
  • zendframework/zend-uri is used by the UriNormalize filter.
  • zendframework/zend-i18n is used by several filters that provide internationalization features.

For our examples, we'll be using the FilterChain functionality, so we will also want to install zend-servicemanager:

$ composer require zendframework/zend-servicemanager

FilterInterface

Filters can be one of two things: a callable that accepts a single argument (the value to filter), or an instance of Zend\Filter\FilterInterface:

namespace Zend\Filter;

interface FilterInterface
{
    public function filter($value);
}

The value can be literally anything, and the filter can return anything itself. Generally speaking, if a filter cannot operate on the value, it is expected to return it verbatim.

zend-filter provides a few dozen filters for common operations, including things like:

  • Normalizing strings, integers, etc. to their corresponding boolean values.
  • Normalizing strings representing integers to integer values.
  • Normalizing empty values to null values.
  • Normalizing input sets representing date and/or time selections from forms to DateTime instances.
  • Normalizing URI values.
  • Comparing values to whitelists and blacklists.
  • Trimming whitespace, stripping newlines, and removing HTML tags or entities.
  • Upper and lower casing words.
  • Stripping everything but digits.
  • Performing PCRE regexp replacements.
  • Word inflection (camel-case to underscores and vice versa, etc.).
  • Decrypting and encrypting file contents, as well as casting file contents to lower or upper case.
  • Compressing and decompressing values.
  • Decrypting and encrypting values.

Any of these may be used by themselves. However, in most cases, if that's all you're doing, you might as well just do the functionality inline. So, what's the benefit of zend-filter?

Chaining filters!

FilterChain

When we get input from the web, it generally comes as strings, and is the result of user input. As such, we often get a lot of garbage: extra spaces, unnecessary newlines, HTML characters, etc.

When filtering such input, we might want to perform several operations:

$value = $request->getParsedBody()['phone'] ?? '';
$value = trim($value);
$value = preg_replace("/[^\n\r]/", '', $value);
$value = preg_replace('/[^\d]/', '', $value);

We then need to test our code to ensure that we're filtering correctly. Additionally, if at any point we fail to re-assign, we may lose the changes we were performing!

With zend-filter, we can instead use a FilterChain. The above example becomes:

use Zend\Filter\FilterChain;

$filter = new FilterChain();
// attachByName uses the class name, minus the namespace, and 
$filter->attachByName('StringTrim');
$filter->attachByName('StripNewlines');
$filter->attachByName('Digits');
$value = $filter->filter($request->getParsedBody()['phone'] ?? '');

Here's another example: let's say we have configuration keys that are in snake_case_format, and which may be read from a file, and we wish to convert those values to CamelCase.

use Zend\Filter;

$filter = new Filter\FilterChain();
// attach lets you provide the instance you wish to use; this will work
// even without zend-servicemanager installed.
$filter->attach(new Filter\StringTrim());
$filter->attach(new Filter\StripNewlines()); // because we may have \r characters
$filter->attach(new Filter\Word\UnderscoreToCamelCase());

$configKeys = array_map([$filter, 'filter'], explode("\n", $fileContents));

This new example demonstrates a key feature of a FilterChain: you can re-use it! Instead of having to put the code for normalizing the values within an array_map callback, we can instead directly use our already configured FilterChain, invoking it once for each value!

Wrapping up

zend-filter can be a powerful tool in your arsenal for dealing with user input. Paired with good validation, you can protect your application from malicious or malformed input.

In the next post, we'll discuss zend-validation!

Save the date!

Want to learn more about Expressive and Zend Framework? What better location than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas, Nevada, USA. Visit the ZendCon website for more information.

Source

When we announced Zend Framework 3 last year, one of the changes was setting the minimum supported PHP version to 5.6. Our initial plan was to support 5.6 until it reaches end-of-life, which occurs 31 December 2018.

PHP 5.6, however, stopped receiving active support almost five months ago, on 19 Jan 2017. This means that it is no longer receiving bugfixes, only critical security fixes. As such, a number of contributors have been pushing for us to up our minimum supported version to support only actively supported PHP versions, which would mean only PHP 7 versions.

In addition to active support, there are other benefits to jumping to PHP 7, including:

  • Scalar type hints
  • Return type hints
  • Null coalescing operator
  • Spaceship operator
  • Anonymous classes
  • Potential for enabling strict type checking

Of course, PHP 7.0 ends active support at the end of this year, on 3 December 2017; moving to 7.0 means that we could anticipate further version bumps in a very short time frame.

PHP 7.1 adds more features and benefits over those in 7.0:

  • Void return types
  • Nullable types
  • "iterable" pseudo-type
  • Non-public class constants
  • Multi-catch exception handling
  • Support for keys in list()
  • Built-in asynchronous signal handling

Further, 7.1 has active support until 1 Dec 2018, giving us a reasonable time frame for supporting the version.

Our view is that the new features in PHP 7 will allow us to simplify our code dramatically, reduce bugs (primarily by increasing type safety), make our code more easily maintainable (less code required to check types; less repetitive code), provide stronger and more predictable interfaces to our users, and simultaneously provide users access to more and better language features.

HHVM

In addition to evaluating PHP 5.6 support, we have also been evaluating our support of HipHop Virtual Machine (HHVM), the alternative PHP runtime maintained by Facebook.

We started running tests against HHVM around the time we released ZF 2, and, for quite some time, required tests to pass except in specific components that used extensions without equivalents in the HHVM ecosystem.

Since we started adopting PHP 5.6, and, in select cases, PHP 7, we've increasingly needed to mark HHVM test jobs as "allowing failure", due to usage of PHP language features that do not exist in HHVM, or incompatibilities in the HHVM runtime.

One initial "selling" feature of HHVM was its performance. However, at this time, PHP 7 competes favorably with HHVM, eliminating one large argument in favor of choosing it. PHP 7.1 also includes many features at the language level (e.g., scalar typehints, return type hints, nullable types, etc.) that many were choosing HHVM for.

Finally, we have found that HHVM usage is an incredibly small percentage of our user base. We have noted that other projects have found similarly, and feel now is the appropriate time to stop supporting HHVM.

Acknowledging the Problems

The biggest downside to adopting PHP 7 releases at this time is one of adoption. Many organizations lock projects to specific infrastructure, including PHP versions. Even with supported repositories such as the Remi RPM repository (for RHEL, CentOS, and Fedora) and Ondřej Debian repository (for Ubuntu and other Debian-based distributions), enterprises still want to pin to distribution-provided PHP versions, which are often literally years out-of-date.

Zend itself offers a supported PHP 7.1 stack via Zend Server, which can be a solution for many of these organizations.

Additionally, with the rise in usage of virtual machines and containers, many of these obstacles are gradually disappearing for organizations. When you can package the entire infrastructure stack, testing and deployment become more predictable, eliminating many of the issues traditionally associated with infrastructure changes. We feel this trend will only increase.

The Plan

As such, our plan is this:

  • We will start releasing new major versions of components that pin to PHP 7.1+, so we can take advantage of the latest language features.

  • We will no longer test against HHVM. Most of our components were failing against HHVM already, so this does not pose a BC break at this time.

  • We will continue providing security patches only to the last release branch supporting PHP 5.6 until PHP 5.6 reaches its end-of-life date. This allows us to minimize the amount of maintenance we need for supporting these versions, while still providing stable, secure releases for our PHP 5 users.

  • New components will pin to PHP 7.1+.

We announced most details of this previously on our newsletter, so some of this work has already begun; we welcome all assistance any of our readers and users can provide in this effort!

On a personal note, I still fondly remember the release of PHP 5; the organization I worked at adopted it fairly early, and I was very excited about the features then. PHP 7 has stoked similar enthusiasm for me, and I'm excited to see what we build for Zend Framework using it!

Save the date!

Want to learn more about Expressive and Zend Framework? What better location than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas, Nevada, USA. Visit the ZendCon website for more information.

Source

With the release of Expressive 2, one of the key stories was the ability to require ZF components within Expressive, and have their dependencies auto-wired into your application courtesy of the component installer.

However, we recently had a user in our Slack channel (need an invite?) indicating they were having issues with usage of custom validators, filters, and input filters. After a more thorough writeup on our forums, I realized we'd missed something important when making these integrations, and set out to solve it.

The problem

In zend-mvc applications, zend-modulemanager aggregates Module classes registered with the application, and, in the process, does things like merge all configuration. One thing it also does is seed plugin managers. The process for this is rather convoluted, as it uses a combination of configuration keys as well as methods defined in Module classes in order to pull together all configuration for a given plugin manager prior to initializing it.

What this means is that, in most cases, components can have a specific configuration key they look under for service definitions specific to a given plugin manager. As an example, for zend-filter, you might do something like:

[
    'filters' => [
        'aliases' => [ /* ... */ ],
        'invokables' => [ /* ... */ ],
        'factories' => [ /* ... */ ],
        'delegators' => [ /* ... */ ],
    ],
]

All plugin manager configurations follow the same format, but use a different top-level key.

The problem we ran into in Expressive is that while we have encouraged users to do this, the plugin managers were never seeded with this configuration, as the logic for that was relegated to zend-modulemanager integrations!

The solution

Over the past few days, I identified seven affected components, and wrote patches for each that fix the problem. What they do is check to see if a zend-modulemanager-specific service is present, and, if not, they then check for and pull the relevant configuration, and use it to configure the plugin manager.

The following releases now have these fixes:

  • zend-log 2.9.2; enables the log_processors, log_writers, log_filters, and log_formatters keys for configuring the ProcessorPluginManager, WriterPluginManager, FilterPluginManager, and FormatterPluginManager, respectively.
  • zend-i18n 2.7.4; enables the translator_plugins key for configuring the Zend\I18n\Translator\LoaderPluginManager.
  • zend-hydrator 2.2.2; enables the hydrators key for configuring the HydratorPluginManager.
  • zend-filter 2.7.2; enables the filters key for configuring the FilterPluginManager.
  • zend-validator 2.9.1; enables the validators key for configuring the ValidatorPluginManager.
  • zend-inputfilter 2.7.4; enables the input_filters key for configuring the InputFilterPluginManager.
  • zend-form 2.10.2; enables the form_elements key for configuring the FormElementManager.

The end result is that each of these components will now work with Expressive out of the box just as they would in a zend-mvc application!

Wrapup

I feel this is a huge step forward in usability of Expressive; in fact, each of these components can now be more easily used in any application, which is a huge win. I'm hoping that with these releases, folks will start evaluating them more seriously for inclusion in other middleware architectures as well.

This problem would likely have not been identified as quickly without the new community process and tools we now have in place. A Slack thread helped identify a problem existed, and a forum post helped us get all the details necessary to reproduce the issue, and then coordinate the patches.

If you have not yet signed up for Slack, you can do so here. If you have not yet visited our forums, head over and start participating!

Save the date!

Want to learn more about Expressive and Zend Framework? What better location than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas, Nevada, USA. Visit the ZendCon website for more information.

Source

Security of your website is not just about mitigating and preventing things like SQL injection; it's also about protecting your users as they browse the site from things like cross-site scripting (XSS) attacks, cross-site request forgery (CSRF), and more. In particular, you need to be very careful about how you generate HTML, CSS, and JavaScript to ensure that you do not create such vectors.

As the mantra goes, filter input, and escape output.

Believe it or not, escaping in PHP is not terribly easy to get right. For example, to properly escape HTML, you need to use htmlspecialchars(), with the flags ENT_QUOTES | ENT_SUBSTITUTE, and provide a character encoding. Who really wants to write

htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'utf-8')

every single time they need to escape a string for use in HTML?

Escaping HTML attributes, CSS, and JavaScript each require a regular expression to identify known problem strings, and a number of heuristics to replace unicode characters with hex entities, each with different rules. While much of this can be done with built-in PHP features, these features do not catch all potential attack vectors. A comprehensive solution is required.

Zend Framework provides the zend-escaper component to manage this complexity for you, exposing functionality for escaping HTML, HTML attributes, JavaScript, CSS, and URLs to ensure they are safe for the browser.

Installation

zend-escaper only requires PHP (of at least version 5.5 at the time of writing), and is installable via composer:

$ composer require zendframework/zend-escaper

Usage

While we considered making zend-escaper act as either functions or static methods, there was one thing in the way: proper escaping requires knowledge of the intended output character set. As such, Zend\Escaper\Escaper must first be instantiated; once it has, you call methods on it.

use Zend\Escaper\Escaper;

$escaper = new Escaper('iso-8859-1');

By default, if no character set is provided, it assumes utf-8; we recommend using UTF-8 unless there is a compelling reason not to. As such, in most cases, you can instantiate it with no arguments:

use Zend\Escaper\Escaper;

$escaper = new Escaper();

The class provides five methods:

  • escapeHtml(string $html) : string will escape the string so it may be safely used as HTML. In general, this means <, >, and & characters (as well as others) are escaped to prevent injection of unwanted tags and entities.
  • escapeHtmlAttr(string $value) : string escapes a string so it may safely be used within an HTML attribute value.
  • escapeJs(string $js) : string escapes a string so it may safely be used within a <script> tag. In particular, this ensures that the code injected cannot contain continuations and escape sequences that lead to XSS vectors.
  • escapeCss(string $css) : string escapes a string to use as CSS within <style> tags; similar to JS, it prevents continuations and escape sequences that can lead to XSS vectors.
  • escapeUrl(string $urlPart) : string escapes a string to use within a URL; it should not be used to escape the entire URL itself. It should be used to escape things such as the URL path, query string parameters, and fragment, however.

So, as examples:

echo $escaper->escapeHtml('<script>alert("zf")</script>');
// results in "&lt;script&gt;alert(&quot;zf&quot;)&lt;/script&gt;"

echo $escaper->escapeHtmlAttr("<script>alert('zf')</script>");
// results in "&lt;script&gt;alert&#x28;&#x27;zf&#x27;&#x29;&lt;&#x2F;script&gt;"

echo $escaper->escapeJs("bar&quot;; alert(&quot;zf&quot;); var xss=&quot;true");
// results in "bar\x26quot\x3B\x3B\x20alert\x28\x26quot\x3Bzf\x26quot\x3B\x29\x3B\x20var\x20xss\x3D\x26quot\x3Btrue"

echo $escaper->escapeCss("background-image: url('/zf.png?</style><script>alert(\'zf\')</script>');");
// results in "background\2D image\3A \20 url\28 \27 \2F zf\2E png\3F \3C \2F style\3E \3C script\3E alert\28 \5C \27 zf\5C \27 \29 \3C \2F script\3E \27 \29 \3B"

echo $escaper->escapeUrl('/foo " onmouseover="alert(\'zf\')');
// results in "%2Ffoo%20%22%20onmouseover%3D%22alert%28%27zf%27%29"

As you can see from these examples, the component aggresively filters each string to ensure it is escaped correctly for the context for which it is intended.

How and where might you use this?

  • Within templates, to ensure output is properly escaped. For example, zend-view includes helpers for it; it would be easy to add such functionality to Plates and other templating solutions.
  • In email templates.
  • In serializers for APIs, to ensure things like URLs or XML attribute data are properly escaped.
  • In error handlers, to ensure error messages are escaped and do not contain XSS vectors.

The main point is that escaping can be easy with zend-escaper; start securing your output today!

Save the date!

Want to learn more about Expressive and Zend Framework? What better location than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas, Nevada, USA. Visit the ZendCon website for more information.

Source

The last couple posts have been around authorization, the act of determining if a given identity has access to a resource. We covered usage of role based access controls, as well as middleware that uses an RBAC.

In this post, we'll explore another option provided by Zend Framework, zend-permissions-acl, which implements Access Control Lists (ACL).

This post will follow the same basic format as the one covering zend-permissions-rbac, using the same basic examples.

Installing zend-permissions-acl

Just as you would any of our components, install zend-permissions-acl via Composer:

$ composer require zendframework/zend-permissions-acl

The component has no requirements at this time other than a PHP version of at least 5.5.

Vocabulary

In ACL systems, we have three concepts:

  • a resource is something to which we control access.
  • a role is something that will request access to a resource.
  • Each resource has privileges for which access will be requested to specific roles.

As an example, an author might request to create (privilege) a blog post (resource); later, an editor (role) might request to edit (privilege) a blog post (resource).

The chief difference to RBAC is that RBAC essentially combines the resource and privilege into a single item. By separating them, you can create a set of discrete permissions for your entire application, and then create roles with multiple-inheritance in order to implement fine-grained permissions.

ACLs

An ACL is created by instantiating the Acl class:

use Zend\Permissions\Acl\Acl;

$acl = new Acl();

Once that instance is available, we can start adding roles, resources, and privileges.

For this blog post, our ACL will be used for a content-based website.

Roles

Roles are added via the $acl->addRole() method. This method takes either a string role name, or a Zend\Permissions\Acl\Role\RoleInterface instance.

Let's start with a "guest" role, that only allows "read" permissions.

use Zend\Permissions\Acl\Role\GenericRole as Role;

// Create some roles
$guest = new Role('guest');
$acl->addRole($guest);

// OR
$acl->addRole('guest');

Referencing roles and resources

Roles are simply strings. We model them as objects in zend-permissions-acl in order to provide strong typing, but the only requirement is that they return a string role name. As such, when creating permissions, you can use either a role instance, or the equivalent name.

The same is true for resources, which we cover in a later section.

By default, zend-permissions-acl implements a whitelist approach. A whitelist denies access to everything unless it is explicitly whitelisted. (This is as opposed to a blacklist, where access is allowed to everything unless it is in the blacklist.) Unless you really know what you're doing we do not suggest toggling this; whitelists are widely regarded as a best practice for security.

What that means is that, out of the gate, while we can do some privilege assertions:

$acl->isAllowed('guest', 'blog', 'read');
$acl->isAllowed('guest', 'blog', 'write');

these will always return false, denying access. So, we need to start adding privileges.

Privileges

Privileges are assigned using $acl->allow().

For the guest role, we'll allow the read privilege on any resource:

$acl->allow('guest', null, 'read');

The second argument to allow() is the resource (or resources); specifying null indicates the privilege applies to all resources. If we re-run the above assertions, we get the following:

$acl->isAllowed('guest', 'blog', 'read');  // true
$acl->isAllowed('guest', 'blog', 'write'); // false

Unknown roles or resources

One thing to note: if either the role or resource used with isAllowed() does not exist, this method raises an exception, specifically a Zend\Permissions\Acl\Exception\InvalidArgumentException, indicating the role or resource could not be found.

In many situations, this may not be what you want; you may want to handle non-existent roles and/or resources gracefully. You could do this in a couple ways. First, you can test to see if the role or resource exists before you check the permissions, using hasRole() and/or hasResource():

if (! $acl->hasRole($foo)) {
    // failed, due to missing role
}
if (! $acl->hasResource($bar)) {
    // failed, due to missing resource
}
if (! $acl->isAllowed($foo, $bar, $privilege)) {
    // failed, due to invalid privilege
}

Alternately, wrap the isAllowed() call in a try/catch block:

try {
    if (! $acl->isAllowed($foo, $bar, $privilege)) {
        // failed, due to missing privileges
    }
} catch (AclInvalidArgumentException $e) {
      // failed, due to missing role or resource
}

Personally, I don't like to use exceptions for application flow, so I recommend the first solution. That said, in most cases, you will be working with a role instance that you've just added to the ACL, and should only perform assertions against known resources.

Resources

Now let's add some actual resources. These are almost exactly like roles in terms of usage: you create a ResourceInterface instance to pass to the ACL, or, more simply, a string; resources are added via the $acl->addResource() method.

use Zend\Permissions\Acl\Resource\GenericResource as Resource;

$resource = new Resource('blog');
$acl->addResource($resource);

// OR:
$acl->addResource('blog');

A resource is anything to which you want to apply permissions. In the remaining examples of this post, we'll use a "blog" as the resource, and provide a variety of permissions related to it.

Inheritance

Let's say we want to build on our previous examples, and create an "editor" role that also incorporates the permissions of the "guest" role, and adds a "write" permission to the "blog" resource.

Unlike RBAC, roles themselves contain no information about inheritance; instead, the ACL takes care of that when you add the role to the ACL:

$editor = new Role('editor');
$acl->addRole($editor, $guest); // OR:
$acl->addRole($editor, 'guest');

The above creates a new role, editor, which inherits the permissions of our guest role. Now, let's add a privilege allowing editors to write to our blog:

$acl->allow('editor', 'blog', 'write');

With this in place, let's do some assertions:

$acl->isAllowed('editor', 'blog', 'write'); // true
$acl->isAllowed('editor', 'blog', 'read');  // true
$acl->isAllowed('guest',  'blog', 'write'); // false

Another role might be a "reviewer" who can "moderate" content:

$acl->addRole('reviewer', 'guest');
$acl->allow('reviewer', 'blog', 'moderate');

$acl->isAllowed('reviewer', 'blog', 'moderate'); // true
$acl->isAllowed('reviewer', 'blog', 'write');    // false; editor only!
$acl->isAllowed('reviewer', 'blog', 'read');     // true
$acl->isAllowed('guest',    'blog', 'moderate'); // false

Let's create another, an "admin" who can do all of the above, but also has permissions for "settings":

$acl->addRole('admin', ['guest', 'editor', 'reviewer']);
$acl->allow('admin', 'blog', 'settings');

$acl->isAllowed('admin',    'blog', 'settings'); // true
$acl->isAllowed('admin',    'blog', 'write');    // true
$acl->isAllowed('admin',    'blog', 'moderate'); // true
$acl->isAllowed('admin',    'blog', 'read');     // true
$acl->isAllowed('editor',   'blog', 'settings'); // false
$acl->isAllowed('reviewer', 'blog', 'settings'); // false
$acl->isAllowed('guest',    'blog', 'write');    // false

Note that the addRole() call here provides an array of roles as the second value this time; when called this way, the new role will inherit the privileges of every role listed; this allows for multiple-inheritance at the role level.

Resource inheritance

Resource inheritance works exactly the same as Role inheritance! Add one or more parent resources when calling addResource() on the ACL, and any privileges assigned to that parent resource will also apply to the new resource.

As an example, I could have a "news" section in my website that has the same privilege and role schema as my blog:

$acl->addResource('news', 'blog');

Fun with privileges!

Privileges are assigned using allow(). Interestingly, like addRole() and addResource(), the role and resource arguments presented may be arrays of each; in fact, so can the privileges themselves!

As an example, we could do the following:

$acl->allow(
    ['reviewer', 'editor'],
    ['blog', 'homepage'],
    ['write', 'maintenance']
);

This would assign the "write" and "maintenance" privileges on each of the "blog" and "homepage" resources to the "reviewer" and "editor" roles! Due to inheritance, the "admin" role would also gain these privileges.

Creating your ACL

When should you create your ACL, exactly? And should it contain all roles and permissions?

Typically, you will create a finite number of application or domain permissions. In our above examples, we could omit the blog resource and apply the ACL only within the blog domain (for example, only within a module of a zend-mvc or Expressive application); alternately, it could be an application-wide ACL, with resources segregated by specific domain within the application.

In either case, you will generally:

  • Create a finite set of well-known roles, resources, and privileges as a global or per-domain ACL.
  • Create a custom role for the current user, typically inheriting from the set of well-known roles.
  • Validate the current user against the ACL.

Unlike RBAC, you typically will not add custom permissions for a user. The reason for this is due to the complexity of storing the combination of roles, resources, and privileges in a database. Storing roles is trivial:

user_id fullname roles
mario Mario editor,reviewer

You could then create the role by splitting the roles field and assigning each as parents:

$acl->addRole($user->getId(), explode(',', $user->getRoles());

However, for fine-grained permissions, you would essentially need an additional lookup table mapping the user to a resource and list of privileges:

user_id resource privileges
mario blog update,delete
mario news update

While it can be done, it's resource and code intensive.

Putting it all together, let's say the user "mario" has logged in, with the role "editor"; further, let's assume that the identity instance for our user implements RoleInterface. If our ACL is already populated per the above examples, I might do the following:

$acl->addRole($mario, $mario->getRoles());

$acl->isAllowed($mario, 'blog', 'settings'); // false; admin only!
$acl->isAllowed($mario, 'blog', 'write');    // true; all editors
$acl->isAllowed($mario, 'blog', 'read');     // true; all guests

Now, let's say we've gone to the work of creating the join table necessary for storing user ACL information; we might have something like the following to further populate the ACL:

foreach ($mario->getPrivileges() as $resource => $privileges) {
    $acl->allow($mario, $resource, explode(',', $privileges));
}

We could then do the following assertions:

$acl->isAllowed($mario,   'blog', 'update'); // true
$acl->isAllowed('editor', 'blog', 'update'); // false; mario only!
$acl->isAllowed($mario,   'blog', 'delete'); // true
$acl->isAllowed('editor', 'blog', 'delete'); // false; mario only!

Custom assertions

Fine-grained as the privilege system can be, sometimes it's not enough.

As an example, we may want to implement a rule that the creator of a content item in our website always has rights to edit the item. How would we implement that with the above system?

zend-permissions-acl allows you to do so via dynamic assertions. Such assertions are classes that implement Zend\Permissions\Acl\Assertion\AssertionInterface, which defines a single method:

namespace Zend\Permissions\Assertion;

use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Resource\ResourceInterface;
use Zend\Permissions\Acl\Role\RoleInterface;

interface AssertionInterface
{
    /**
     * @return bool
     */
    public function assert(
        Acl $acl,
        RoleInterface $role = null,
        ResourceInterface $resource = null,
        $privilege = null
    );
}

For the sake of this example, let's assume:

  • We cast our identity to a RoleInterface instance after retrieval.
  • The content item is represented as an object.
  • The object has a method getCreatorUsername() that will return the same username as we might have in our custom identity from the previous example.
  • If the username is the same as the custom identity, allow any privileges.

Because we have PHP 7 at our disposal, we'll create the assertion as an anonymous class:

use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Assertion\AssertionInterface;
use Zend\Permissions\Acl\Resource\ResourceInterface;
use Zend\Permissions\Acl\Role\RoleInterface;

$assertion = new class ($identity, $content) implements AssertionInterface {
    private $content;
    private $identity;

    public function __construct(RoleInterface $identity, $content)
    {
        $this->identity = $identity;
        $this->content = $content;
    }

    /**
     * @return bool
     */
    public function assert(
        Acl $acl,
        RoleInterface $role = null,
        ResourceInterface $resource = null,
        $privilege = null
    ) {
        if (null === $role || $role->getRoleId() !== $this->identity->getRoleId()) {
            return false;
        }

        if (null === $resource || 'blog' !== $resource->getResourceId()) {
            return false;
        }

        return $this->identity->getRoleId() === $this->content->getCreatorUsername();
    }
};

// Attach the assertion to all roles on the blog resource;
// custom assertions are provided as a fourth argument to allow().
$acl->allow(null, 'blog', null, $assertion);

$acl->isAllowed('mario', 'blog', 'edit'); // returns true if $mario created $content

The above creates a new assertion that will trigger for the "blog" resource when a privilege we do not already know about is queried. In that particular case, if the creator of our content is the same as the current user, it will return true, allowing access!

By creating such assertions in-place with data retrieved at runtime, you can achieve an incredible amount of flexibility for your ACLs.

Wrapping up

zend-permissions-acl provides a huge amount of power, and the ability to provide both role and resource inheritance can vastly simplify setup of complex ACLs. Additionally, the privilege system provides much-needed granularity.

If you wanted to use ACLs in middleware, the usage is quite similar to zend-permissions-rbac: inject your ACL instance in your middleware, retrieve your user identity (and thus role) from the request, and perform queries against the ACL using the current middleware or route as a resource, and either the HTTP method or the domain action you will perform as the privilege.

The main difficulty with zend-permissions-acl is that there is no 1:1 relationship between a role and a privilege, which makes storing ACL information in a database more complex. If you find yourself struggling with that fact, you may want to use RBAC instead.

Hopefully these last few posts provide you with the information you need to start adding authentication and authorization to any PHP application you're writing!

Save the date!

Want to learn more about Expressive and Zend Framework? What better location than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas, Nevada, USA. Visit the ZendCon website for more information.

Source

In a previous post, we demonstrated how to authenticate a middleware application in PHP. In this post we will continue the discussion, showing how to manage authorizations.

We will start from an authenticated user and demonstrate how to allow or disable actions for specific users. We will collect users by groups and we will use a Role-Based Access Control (RBAC) system to manage the authorizations.

To implement RBAC, we will consume zendframework/zend-permissions-rbac.

If you are not familiar with RBAC and the usage of zend-permissions-rbac, you can read our previous blog post on the subject.

Getting started

This article assumes you have already created the Auth module, as described in our previous post on authentication. For the purposes of our application, we'll create a new module, Permission, in which we'll put our classes, middleware, and general configuration.

First, if you have not already, install the tooling support:

$ composer require --dev zendframework/zend-expressive-tooling

Next, we'll create the Permission module:

$ ./vendor/bin/expressive module:create Permission

With that out of the way, we can get started.

Authentication

As already mentioned, we will reuse the Auth module created in our previous post. We will reuse the Auth\Action\AuthAction::class to get the authenticated user's data.

Authorization

In order to manage authorization, we will use a RBAC system using the user's role. A user's role is a string that represents the permission level; as an example, the role administrator might provide access to all permissions.

In our scenario, we want to allow or disable access of specific routes to a role or set of roles. Each route represents a permission in RBAC terminology.

We can use zendframework/zend-permissions-rbac to manage the RBAC system using a PHP configuration file storing the list of roles and permissions. Using zend-permissions-rbac, we can also manage permissions inheritance.

For instance, imagine implementing a blog application; we might define the following roles:

  • administrator
  • editor
  • contributor

A contributor can create, edit, and delete only the posts created by theirself. The editor can create, edit, and delete all posts and publish posts (that means enabling public view of a post in the web site). The administrator can perform all actions, including changing the blog's configuration.

This is a perfect use case for using permission inheritance. In fact, the administrator role would inherit the permissions of the editor, and the editor role inherits the permissions of the contributor.

To manage the previous scenario, we can use the following configuration file:

// In src/Permission/config/rbac.php:

return [
    'roles' => [
        'administrator' => [],
        'editor'        => ['admin'],
        'contributor'   => ['editor'],
    ],
    'permissions' => [
        'contributor' => [
            'admin.dashboard',
            'admin.posts',
        ],
        'editor' => [
            'admin.publish',
        ],
        'administrator' => [
            'admin.settings',
        ],
    ],
];

In this file we have specified 3 roles, including the inheritance relationship using an array of role names. The parent of administator is an empty array, meaning no parents.

The permissions are configured using the permissions key. Each role has the list of permissions, specified with an array of route names.

All the roles can access the route admin.dashboard and admin.posts. The editor role can also access admin.publish. The administrator can access all the roles of contributor and editor. Moreover, only the administrator can access the admin.settings route.

We used the route names as RBAC permissions because in this way we can allow URL and HTTP methods using a single resource name. Moreover, in Expressive we have a config/routes.php file containing all the routes and we can easily use it to add authorization, as we did for authentication.

Authorization middleware

Now that we have the RBAC configuration in place, we can create a middleware that performs the user authorization verifications.

We can create an AuthorizationAction middleware in our Permission module as follows:

// in src/Permission/src/Action/AuthorizationAction.php:

namespace Permission\Action;

use Auth\Action\AuthAction;
use Interop\Http\ServerMiddleware\DelegateInterface;
use Interop\Http\ServerMiddleware\MiddlewareInterface as MiddlewareInterface;
use Permission\Entity\Post as PostEntity;
use Permission\Service\PostService;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\Response\EmptyResponse;
use Zend\Expressive\Router\RouteResult;
use Zend\Permissions\Rbac\AssertionInterface;
use Zend\Permissions\Rbac\Rbac;
use Zend\Permissions\Rbac\RoleInterface;

class AuthorizationAction implements MiddlewareInterface
{
    private $rbac;
    private $postService;

    public function __construct(Rbac $rbac, PostService $postService)
    {
        $this->rbac        = $rbac;
        $this->postService = $postService;
    }

    public function process(ServerRequestInterface $request, DelegateInterface $delegate)
    {
        $user = $request->getAttribute(AuthAction::class, false);
        if (false === $user) {
            return new EmptyResponse(401);
        }

        // if a post attribute is present and user is contributor
        $postUrl = $request->getAttribute('post', false);
        if (false !== $postUrl && $user['role'] === 'contributor') {
            $post = $this->postService->getPost($postUrl);
            $assert = new class ($user['username'], $post) implements AssertionInterface {
                private $post;
                private $username;

                public function __construct(string $username, PostEntity $post)
                {
                    $this->username = $username;
                    $this->post     = $post;
                }

                public function assert(Rbac $rbac)
                {
                    return $this->username === $this->post->getAuthor();
                }
            };
        }

        $route     = $request->getAttribute(RouteResult::class);
        $routeName = $route->getMatchedRoute()->getName();
        if (! $this->rbac->isGranted($user['role'], $routeName, $assert ?? null)) {
            return new EmptyResponse(403);
        }

        return $delegate->process($request);
    }
}

If the user is not present, the AuthAction::class attribute will be false. In this case we are returning a 401 error, indicating we have an unauthenticated user, and halting execution.

If a user is returned from AuthAction::class attribute, this means that we have an authenticated user.

The authentication is performed by the Auth\Action\AuthAction class that stores the AuthAction::class attribute in the request. See the previous post for more information.

This middleware performs the authorization check using isGranted($role, $permission) where $role is the user's role ($user['role']) and $permission is the route name, retrieved by the RouteResult::class attribute. If the role is granted, we continue the execution flow with the delegate middleware. Otherwise, we stop the execution with a 403 error, indicating lack of authorization.

We manage also the case when the user is a contributor and there is a post attribute in the request (e.g. /admin/posts/{post}). That means someone is performing some action on a specific post. To perform this action, we require that the owner of the post should be the same as the authenticated user.

This will prevent a contributor to change the content of a post if he/she is not the author. We managed this special case using a dynamic assertion, built using an anonymous class; it checks if the authenticated username is the same of the author's post. We used a general PostEntity class with a getAuthor() function.

In order to retrieve for the route name, we used the RouteResult::class attribute provided by Expressive. This attribute facilitates access to the matched route.

The AuthorizationAction middleware requires the Rbac and the PostService dependencies. The first is an instance of Zend\Permissions\Rbac\Rbac and the second is a general service to manage blog posts, i.e. a class that performs some lookup to retrieve the post data from a database.

To inject these dependencies, we use an AuthorizationFactory like the following:

namespace Permission\Action;

use Interop\Container\ContainerInterface;
use Zend\Permissions\Rbac\Rbac;
use Zend\Permissions\Rbac\Role;
use Permission\Service\PostService;
use Exception;

class AuthorizationFactory
{
    public function __invoke(ContainerInterface $container)
    {
        $config = $container->get('config');
        if (! isset($config['rbac']['roles'])) {
            throw new Exception('Rbac roles are not configured');
        }
        if (!isset($config['rbac']['permissions'])) {
            throw new Exception('Rbac permissions are not configured');
        }

        $rbac = new Rbac();
        $rbac->setCreateMissingRoles(true);

        // roles and parents
        foreach ($config['rbac']['roles'] as $role => $parents) {
            $rbac->addRole($role, $parents);
        }

        // permissions
        foreach ($config['rbac']['permissions'] as $role => $permissions) {
            foreach ($permissions as $perm) {
                $rbac->getRole($role)->addPermission($perm);
            }
        }
        $post = $container->get(PostService::class);

        return new AuthorizationAction($rbac, $post);
    }
}

This factory class builds the Rbac object using the configuration file stored in src/Permission/config/rbac.php. We read all the roles and the permissions following the order in the array. It is important to enable the creation of missing roles in the Rbac object using the function setCreateMissingRoles(true). This is required to be sure to create all the roles even if we add it out of order. For instance, without this setting, the following configuration will throw an exception:

return [
    'roles' => [
        'contributor'   => ['editor'],
        'editor'        => ['administrator'],
        'administrator' => []
    ]
]

because the editor and the administrator roles are specified as parents of other roles before they were created.

Finally, we can configure the Permission module adding the following dependencies:

// In src/Permission/src/ConfigProvider.php:

// Update the following methods:
public function __invoke()
{
    return [
        'dependencies' => $this->getDependencies(),
        'rbac'         => include __DIR__ . '/../config/rbac.php',
    ];
}

public function getDependencies()
{
    return [
        'factories' => [
            Service\PostService::class => Service\PostFactory::class,
            Action\AuthorizationAction::class => Action\AuthorizationFactory::class,
        ],
    ];
}

Configure the route for authorization

To enable authorization on a specific route, we need to add the Permission\Action\AuthorizationAction middleware in the route, as follows:

$app->get('/admin/dashboard', [
    Auth\Action\AuthAction::class,
    Permission\Action\AuthorizationAction::class,
    Admin\Action\DashboardAction::class
], 'admin.dashboard');

This is an example of the GET /admin/dashboard route with admin.dashboard as the name. We add AuthAction and AuthorizationAction before execution of the DashboardAction. The order of the middleware array is important; authentication must happen first, and authorization must happen before executing the dashboard middleware.

Add the AuthorizationAction middleware to all routes requiring authorization.

Conclusion

This article, together with the one related to the authentication middleware, demonstrates how to accomodate authentication and authorization within middleware in PHP.

We showed how to create two separate Expressive modules, Auth and Permission, to provide authentication and authorization using zend-authentication and zend-permissions-rbac.

We showed also the usage of a dynamic assertion for specific permissions based on the role and username of an authenticated user.

The blog use case proposed in this article is quite simple, but the architecture used can be applied also in complex scenario, to manage permissions based on different requirements.

In the future we will talk again about authentication and authorization, since this is a very important aspect of web applications.

Save the date!

Want to learn more about Expressive and Zend Framework? What better location than ZendCon 2017! ZendCon will be hosted 23-26 October 2017 in Las Vegas, Nevada, USA. Visit the ZendCon website for more information.

Source

ZF2016-04: Potential remote code execution in zend-mail via Sendmail adapter

When using the zend-mail component to send email via the Zend\Mail\Transport\Sendmail transport, a malicious user may be able to inject arbitrary parameters to the system sendmail program. The attack is performed by providing additional quote characters within an address; when unsanitized, they can be interpreted as additional command line arguments, leading to the vulnerability.

The following example demonstrates injecting additional parameters to the sendmail binary via the From address:

use Zend\Mail;

$mail = new Mail\Message();
$mail->setBody('This is the text of the email.');

// inject additional parameters to sendmail command line
$mail->setFrom('"AAA\" params injection"@domain', 'Sender\'s name');

$mail->addTo('hacker@localhost', 'Name of recipient');
$mail->setSubject('TestSubject');

$transport = new Mail\Transport\Sendmail();
$transport->send($mail);

The attack works because zend-mail filters the email addresses using the RFC 3696 specification, where the string "AAA\" params injection"@domain is considered a valid address. This validation is provided using the zend-validator component with the following parameters:

Zend\Validator\EmailAddress(
    Zend\Validator\Hostname::ALLOW_DNS | Zend\Validator\Hostname::ALLOW_LOCAL
)

The above accepts local domain with any string specified by double quotes as the local part. While this is valid per RFC 3696, due to the fact that sender email addresses are provided to the sendmail binary via the command line, they create the vulnerability described above.

Action Taken

To fix the issue, we added a transport-specific email filter for the From header in the Sendmail transport adapter. The filter checks for the sequence \" in the local part of the email From address.

$from = $headers->get('From');
if ($from) {
    foreach ($from->getAddressList() as $address) {
        if (preg_match('/\\\"/', $address->getEmail())) {
            throw new Exception\RuntimeException("Potential code injection in From header");
        }
    }
}

The patch resolving the vulnerability is available in:

  • zend-mail, starting in version 2.7.2
  • zend-mail, 2.4.11
  • Zend Framework, 2.4.11

Zend Framework 2.5 and 3.0 versions will receive the update automatically, as executing composer update in proejcts using these versions will update to zend-mail 2.7.2+.

Acknowledgments

The Zend Framework team thanks the following for identifying the issues and working with us to help protect its users:

  • The independent security researcher Dawid Golunski, who reported the vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program;
  • Enrico Zimuel, who provided the patch.

Source

The Zend Framework community is pleased to announce the immediate availability of Zend Framework 1.12.20! Packages and installation instructions are available at:

Changelog

This release includes a single security patch, reported as ZF2016-03, for SQL injection vulnerabilities in the Zend_Db_Select::order() and Zend_Db_Select::group() methods. If you use these, we recommend updating immedaitely.

To see the complete set of issues resolved for 1.12.20, please visit the changelog:

End of Life

As announced previously, Zend Framework 1 will reach its end of life on 28 September 2016, and only receive security fixes between now and that date. Past that point, we will offer custom bug and security fixes for Zend Framework 1 on-demand only to Enterprise users of Zend Server.

Thank You!

Many thanks to Enrico Zimuel for his efforts in developing the security patch for this release!

Source