Paginating data collections with zend-paginator

zend-paginator is a flexible
component for paginating collections of data and presenting that data to users.

Pagination is a standard UI solution
to manage the visualization of lists of items, like a list of posts in a blog
or a list of products in an online store.

zend-paginator is very popular among Zend Framework developers, and it’s often
used with zend-view, thanks to
the pagination control view helper zend-view provides.

It can be used also with other template engines. In this article, I will
demonstrate how to use it with Plates.

Usage of zend-paginator

The component can be installed via composer, using
the following command:

$ composer require zendframework/zend-paginator

To consume the paginator component, we need a collection of items.
zend-paginator ships with several different adapters for common collection
types:

  • ArrayAdapter, which works with PHP arrays;
  • Callback, which allows providing callbacks for obtaining counts of items and lists of items;
  • DbSelect, to work with a SQL collection (using zend-db);
  • DbTableGateway, to work with a Table Data Gateway (using the TableGateway
    feature from zend-db.
  • Iterator, to work with any Iterator instance.

If your collection does not fit one of these adapters, you can create a custom
adapter. To do so, you will need to implement
ZendPaginatorAdapterAdapterInterface, which defines two methods:

  • count() : int
  • getItems(int $offset, int $itemCountPerPage) : array

Each adapter need to return the total number of items in the collection,
implementing the count() method, and a portion (a page) of items starting
from $offset position with a size of $itemCountPerPage per page.

With these two methods we can use zend-paginator with any type of collection.

For instance, imagine we need to paginate a collection of blog posts and we
have a Posts class that manage all the posts. We can implement an adapter
like this:

require 'vendor/autoload.php';

use ZendPaginatorAdapterAdapterInterface;
use ZendPaginatorPaginator;
use ZendPaginatorScrollingStyleSliding;

class Posts implements AdapterInterface
{
    protected $posts = [];

    public function __construct()
    {
      // Read posts from file/database/whatever
    }

    public function count()
    {
        return count($this->posts);
    }

    public function getItems($offset, $itemCountPerPage)
    {
        return array_slice($this->posts, $offset, $itemCountPerPage);
    }
}

$posts = new Posts();
$paginator = new Paginator($posts);

Paginator::setDefaultScrollingStyle(new Sliding());
$paginator->setCurrentPageNumber(1);
$paginator->setDefaultItemCountPerPage(8);

foreach ($paginator as $post) {
  // Iterate on each post
}

$pages = $paginator->getPages();
var_dump($pages);

In this example, we created a zend-paginator adapter using a custom Posts
class. This class stores the collection of posts using a protected array
($posts). This adapter is then passed to an instance of Paginator.

When creating a Paginator, we need to configure its behavior.
The first setting is the scrolling style. In the example above, we used the
Sliding
style, a Yahoo!-like scrolling style that positions the current page number as
close as possible to the center of the page range.

Scrolling style

Note: the Sliding scrolling style is the default style used by zend-paginator. We need
to set it explicitly using Paginator::setDefaultScrollingStyle() only if we
do not use zend-servicemanager
as a plugin manager. Otherwise, the scrolling style is loaded by default from
the plugin manager.

The other two configuration values are the current page number and the number
of items per page. In the example above, we started from page 1, and we count 8
items per page.

We can then iterate on the $paginator object to retrieve the post of the
current page in the collection.

At the end, we can retrieve the information regarding the previous page, the
next page, the total items in the collection, and more. To get these values
we need to call the getPages() method. We will obtain an object like this:

object(stdClass)#81 (13) {
  ["pageCount"]=>
  int(3)
  ["itemCountPerPage"]=>
  int(8)
  ["first"]=>
  int(1)
  ["current"]=>
  int(1)
  ["last"]=>
  int(3)
  ["next"]=>
  int(2)
  ["pagesInRange"]=>
  array(3) {
    [1]=>
    int(1)
    [2]=>
    int(2)
    [3]=>
    int(3)
  }
  ["firstPageInRange"]=>
  int(1)
  ["lastPageInRange"]=>
  int(3)
  ["currentItemCount"]=>
  int(8)
  ["totalItemCount"]=>
  int(19)
  ["firstItemNumber"]=>
  int(1)
  ["lastItemNumber"]=>
  int(8)
}

Using this information, we can easily build an HTML footer to navigate across
the collection.

Note: using zend-view, we can consume the paginationControl
helper, which emits an HTML pagination bar.

In the next section, I’ll demonstrate using the Plates
template engine.

An example using Plates

Plates implements templates using native PHP; it is fast and easy to use,
without any additional meta language; it is just PHP.

In our example, we will create a Plates template to paginate a collection of
data using zend-paginator. We will use bootstrap as
the UI framework.

For purposes of this example, blog posts will be accessible via the following URL:

/blog[/page/{page:d+}]

where [/page/{page:d+}] represents the optional page number (using the regexp
d+ to validate only digits). If we open the /blog URL we will get the
first page of the collection. To return the second page we need to connect to
/blog/page/2, third page to /blog/page/3, and so on.

For instance, we can manage the page parameter using a PSR-7 middleware class
consuming the previous Posts adapter, that works as follow:

use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;
use LeaguePlatesEngine;
use ZendPaginatorPaginator;
use ZendPaginatorScrollingStyleSliding;
use Posts;

class PaginatorMiddleware
{
    /** @var Posts */
    protected $posts;

    /** @var Engine */
    protected $template;

    public function __construct(Posts $post, Engine $template = null)
    {
        $this->posts    = $post;
        $this->template = $template;
    }

    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response, callable $next = null
    ) {
        $paginator = new Paginator($this->posts);
        $page = $request->getAttribute('page', 1);

        Paginator::setDefaultScrollingStyle(new Sliding());
        $paginator->setCurrentPageNumber($page);
        $paginator->setDefaultItemCountPerPage(8);

        $pages = $paginator->getPages();
        $response->getBody()->write(
            $this->template->render('posts', [
                'paginator' => $paginator,
                'pages' => $pages,
            ])
        );
        return $response;
    }
}

We used a posts.php template, passing the paginator ($paginator) and
the pages ($pages) instances. That template could then look like the following:

<?php $this->layout('template', ['title' => 'Blog Posts']) ?>

<div class="container">
  <h1>Blog Posts</h1>

  <?php foreach ($paginator as $post): ?>
    <div class="row">
      <?php // prints the post title, date, author, ... ?>
    </div>
  <?php endforeach; ?>

  <?php $this->insert('page-navigation', ['pages' => $pages]) ?>
</div>

The page-navigation.php template contains the HTML code for the page
navigation control, with button like previous, next, and page numbers.

<nav aria-label="Page navigation">
  <ul class="pagination">
    <?php if (! isset($pages->previous)): ?>
      <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
    <?php else: ?>
      <li><a href="/blog/page/<?php echo $pages->previous ?>" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>
    <?php endif; ?>

    <?php foreach ($pages->pagesInRange as $num): ?>
      <?php if ($num === $pages->current): ?>
        <li class="active"><a href="/blog/page/<?php echo $num ?>"><?php echo $num ?> <span class="sr-only">(current)</span></a></li>
      <?php else: ?>
        <li><a href="/blog/page/<?php echo $num ?>"><?php echo $num ?></a></li>
      <?php endif; ?>
    <?php endforeach; ?>

    <?php if (! isset($pages->next)): ?>
      <li class="disabled"><a href="#" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>
    <?php else: ?>
      <li><a href="/blog/page/<?php echo $pages->next ?>" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>
    <?php endif; ?>
  </ul>
</nav>

Summary

The zend-paginator component of Zend Framework is a powerful and easy to use
package that provides pagination of data. It can be used as standalone component
in many PHP projects using different frameworks and template engines. In this
article, I demonstrated how to use it in general purpose applications.
Moreover, I showed an example using Plates and Bootstrap, in a PSR-7 middleware
scenario.

Visit the zend-paginator documentation
to find out what else you might be able to do with this component!

Source: Zend feed

Implement a SOAP server with zend-soap

zend-soap provides a full-featured
SOAP implementation. SOAP is an XML-based
web protocol designed to allow describing messages, and, optionally,
operations to perform. It’s similar to XML-RPC,
but with a few key differences:

  • Arbitrary data structures may be described; you are not limited to the basic
    scalar, list, and struct types of XML-RPC. Messages are often serializations
    of specific object types on either or both the client and server. The
    SOAP message may include information on its own structure to allow the server
    or client to determine how to interpret the message.

  • Multiple operations may be described in a message as well, versus the one
    call, one operation structure of XML-RPC.

In other words, it’s an extensible protocol. This provides obvious benefits,
but also a disadvantage: creating and parsing SOAP messages can quickly become
quite complex!

To alleviate that complexity, Zend Framework provides the zend-soap component,
which includes a server implementation.

Why these articles on RPC services?

In the past couple weeks, we’ve covered JSON-RPC
and XML-RPC. One feedback we’ve
seen is: why bother — shouldn’t we be creating REST services instead?

We love REST; one of our projects is Apigility, which
allows you to simply and quickly build REST APIs. However, there are occasions
where RPC may be a better fit:

  • If your services are less resource oriented, and more function oriented
    (e.g., providing calculations).

  • If consumers of your services may need more uniformity in the service
    architecture in order to ensure they can quickly and easily consume the
    services, without needing to create unique tooling for each service exposed.
    While the goal of REST is to offer discovery, when every payload to send or
    receive is different, this can often lead to an explosion of code when
    consuming many services.

  • Some organizations and companies may standardize on certain web service
    protocols due to existing tooling, ability to train developers, etc.

While REST may be the preferred way to architect web services, these and
other reasons often dictate other approaches. As such, we provide these RPC
alternatives for PHP developers.

What benefits does it offer over the PHP extension?

PHP provides SOAP client and server capabilities already via its
SOAP extension; why do we offer a component?

By default, PHP’s SoapServer::handle() will:

  • Grab the POST body (php://input), unless an XML string is passed to it.
  • Emit the headers and SOAP XML response body to the output buffer.

Exceptions or PHP errors raised during processing may result in a SOAP fault
response, with no details, or can result in invalid/empty SOAP responses
returned to the client.

The primary benefit zend-soap provides, then, is error handling. You can
whitelist exception types, and, when encountered, fault responses containing the
exception details will be returned. PHP errors will be emitted as SOAP faults.

The next thing that zend-soap offers is WSDL generation. WSDL allows you to
describe the web services you offer, so that clients know how to work with
your services. ext/soap provides no functionality around creating WSDL; it
simply expects that you will have a valid one for use with the client or server.

zend-soap provides an AutoDiscover class that uses reflection on the classes
and functions you pass it in order to build a valid WSDL for you; you can then
provide this to your server and your clients.

Creating a server

There are two parts to providing a SOAP server:

  • Providing the server itself, which will handle requests.
  • Providing the WSDL.

Building each follows the same process; you simply emit them with different HTTP
Content-Type headers, and under different HTTP methods (the server will always
react to POST requests, while WSDL should be available via GET).

First, let’s define a function for populating a server instance with classes
and functions:

use AcmeModel;

function populateServer($server, array $env)
{
    // Expose a class and its methods:
    $server->setClass(ModelCalculator::class);

    // Expose an object instance and its methods:
    $server->setObject(new ModelEnv($env));

    // Expose a function:
    $server->addFunction('AcmeModelping');
}

Note that $server is not type-hinted; that will become more obvious soon.

Now, let’s assume that the above function is available to us, and use it to
create our WSDL:

// File /soap/wsdl.php

use ZendSoapAutoDiscover;

if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
    // Only handle GET requests
    header('HTTP/1.1 400 Client Error');
    exit;
}

$wsdl = new AutoDiscover();
populateServer($wsdl, $_ENV);
$wsdl->handle();

Done! The above will emit the WSDL for either the client or server to consume.

Now, let’s create the server. The server requires a few things:

  • The public, HTTP-accessible location of the WSDL.
  • SoapServer options, including the actor URI for the server and SOAP
    version targeted.

Additionally, we’ll need to notify the server of its capabilities, via the
populateServer() function.

// File /soap/server.php

use ZendSoapServer;

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    // Only handle POST requests
    header('HTTP/1.1 400 Client Error');
    exit;
}

$server = new Server(dirname($_SERVER['REQUEST_URI']) . '/wsdl.php', [
    'actor' => $_SERVER['REQUEST_URI'],
]);

populateServer($server, $_ENV);
$server->handle();

The reason for the lack of type-hint should now be clear; both the Server and
AutoDiscover classes have the same API for populating the instances with
classes, objects, and functions; having a common function for doing so allows us
to ensure the WSDL and server do not go out of sync.

From here, you can point your clients at /soap/server.php on your domain, and
they will have all the information they need to work with your service.

Using zend-soap within a zend-mvc application

The above details an approach using vanilla PHP; what about using zend-soap
within a zend-mvc context?

To do this, we’ll need to learn a few more things.

First, you can provide Server::handle() with the request to process. This
must be one of the following:

  • a DOMDocument
  • a DOMNode
  • a SimpleXMLElement
  • an object implementing __toString(), where that method returns an XML string
  • an XML string

We can grab this information from the MVC request instance’s body content.

Second, we will need the server to return the response, so we can use it to
populate the MVC response instance. We can do that by calling
Server::setReturnResponse(true). When we do, Server::handle() will return an
XML string representing the SOAP response message.

Let’s put it all together:

namespace AcmeController;

use AcmeModel;
use ZendSoapAutoDiscover as WsdlAutoDiscover;
use ZendSoapServer as SoapServer;
use ZendMvcControllerAbstractActionController;

class SoapController extends AbstractActionController
{
    private $env;

    public function __construct(ModelEnv $env)
    {
        $this->env = $env;
    }

    public function wsdlAction()
    {
        /** @var ZendHttpRequest $request */
        $request = $this->getRequest();

        if (! $request->isGet()) {
            return $this->prepareClientErrorResponse('GET');
        }

        $wsdl = new WsdlAutoDiscover();
        $this->populateServer($wsdl);

        /** @var ZendHttpResponse $response */
        $response = $this->getResponse();

        $response->getHeaders()->addHeaderLine('Content-Type', 'application/wsdl+xml');
        $response->setContent($wsdl->toXml());
        return $response;
    }

    public function serverAction()
    {
        /** @var ZendHttpRequest $request */
        $request = $this->getRequest();

        if (! $request->isPost()) {
            return $this->prepareClientErrorResponse('POST');
        }

        // Create the server
        $server = new SoapServer(
            $this->url()
                ->fromRoute('soap/wsdl', [], ['force_canonical' => true]),
            [
                'actor' => $this->url()
                    ->fromRoute('soap/server', [], ['force_canonical' => true]),
            ]
        );
        $server->setReturnResponse(true);
        $this->populateServer($server);

        $soapResponse = $server->handle($request->getContent());

        /** @var ZendHttpResponse $response */
        $response = $this->getResponse();

        // Set the headers and content
        $response->getHeaders()->addHeaderLine('Content-Type', 'application/soap+xml');
        $response->setContent($soapResponse);
        return $response;
    }

    private function prepareClientErrorResponse($allowed)
    {
        /** @var ZendHttpResponse $response */
        $response = $this->getResponse();
        $response->setStatusCode(405);
        $response->getHeaders()->addHeaderLine('Allow', $allowed);
        return $response;
    }

    private function populateServer($server)
    {
        // Expose a class and its methods:
        $server->setClass(ModelCalculator::class);
    
        // Expose an object instance and its methods:
        $server->setObject($this->env);
    
        // Expose a function:
        $server->addFunction('AcmeModelping');
    }
}

The above assumes you’ve created routes soap/server and soap/wsdl, and uses
those to generate the URIs for the server and WSDL, respectively; the
soap/server route should map to the SoapController::serverAction() method
and the soap/wsdl route should map to the SoapController::wsdlAction()
method.

Inject your dependencies!

You’ll note that the above example accepts the AcmeModelEnv instance via
its constructor, allowing us to inject a fully-configured instance into the
server and/or WSDL autodiscovery. This means that you will need to provide a
factory for your controller, to ensure that it is injected with a fully
configured instance — and that likely also means a factory for the
model, too.

To simplify this, you may want to check out the ConfigAbstractFactory
or ReflectionBasedAbstractFactory,
both of which were introduced in version 3.2.0 of zend-servicemanager.

Using zend-soap within PSR-7 middleware

Using zend-soap in PSR-7 middleware is essentially the same as what we detail
for zend-mvc: you’ll need to pull the request content for the server, and use
the SOAP response returned to populate a PSR-7 response instance.

The example below assumes the following:

  • You are using the UrlHelper and ServerUrlHelper from zend-expressive-helpers
    to generate URIs.
  • You are routing to each middleware such that:
    • The ‘soap.server’ route will map to the SoapServerMiddleware, and only
      allow POST requests.
    • The ‘soap.wsdl’ route will map to the WsdlMiddleware, and only
      allow GET requests.
namespace AcmeMiddleware;

use AcmeModel;
use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;
use ZendDiactorosResponseTextResponse;
use ZendSoapAutoDiscover as WsdlAutoDiscover;
use ZendSoapServer as SoapServer;

trait Common
{
    private $env;

    private $urlHelper;

    private $serverUrlHelper;

    public function __construct(
        ModelEnv $env,
        UrlHelper $urlHelper,
        ServerUrlHelper $serverUrlHelper
    ) {
        $this->env = $env;
        $this->urlHelper = $urlHelper;
        $this->serverUrlHelper = $serverUrlHelper;
    }

    private function populateServer($server)
    {
        // Expose a class and its methods:
        $server->setClass(ModelCalculator::class);
    
        // Expose an object instance and its methods:
        $server->setObject($this->env);
    
        // Expose a function:
        $server->addFunction('AcmeModelping');
    }
}

class SoapServerMiddleware
{
    use Common;

    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next
    ) {
        $server = new SoapServer($this->generateUri('soap.wsdl'), [
            'actor' => $this->generateUri('soap.server')
        ]);
        $server->setReturnResponse(true);
        $this->populateServer($server);

        $xml = $server->handle((string) $request->getBody());

        return new TextResponse($xml, 200, [
            'Content-Type' => 'application/soap+xml',
        ]);
    }

    private function generateUri($route)
    {
        return ($this->serverUrlHelper)(
            ($this->urlHelper)($route)
        );
    }
}

class WsdlMiddleware
{
    use Common;

    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next
    ) {
        $server = new WsdlAutoDiscover();
        $this->populateServer($server);

        return new TextResponse($server->toXml(), 200, [
            'Content-Type' => 'application/wsdl+xml',
        ]);
    }
}

Since each middleware has the same basic construction, I’ve created a trait with
the common functionality, and composed it into each middleware. As you will
note, the actual work of each middleware is relatively simple; create a server,
and marshal a resposne to return.

In the above example, I use the zend-diactoros-specific
TextResponse type to generate the response; this could be any other response
type, as long as the Content-Type header is set correctly, and the status code
is set to 200.

Per the note above, you will need to
configure your dependency injection container to inject the middleware instances
with the model and helpers.

Summary

While SOAP is often maligned in PHP circles, it is still in wide use within
enterprises, and used in many cases to provide cross-platform web services with
predictable behaviors. It can be quite complex, but zend-soap helps smooth out
the bulk of the complexity. You can use it standalone, within a Zend Framework
MVC application, or within any application framework you might be using.

Visit the zend-soap documentation
to find out what else you might be able to do with this component!

Source: Zend feed

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

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

When using the zend-mail component
to send email via the ZendMailTransportSendmail 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 ZendMail;

$mail = new MailMessage();
$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 MailTransportSendmail();
$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:

ZendValidatorEmailAddress(
    ZendValidatorHostname::ALLOW_DNS | ZendValidatorHostname::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 ExceptionRuntimeException("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: Zend security feed

ZF2016-03: Potential SQL injection in ORDER and GROUP functions of ZF1

ZF2016-03: Potential SQL injection in ORDER and GROUP functions of ZF1

The implementation of ORDER BY and GROUP BY in Zend_Db_Select remained
prone to SQL injection when a combination of SQL expressions and comments were
used. This security patch provides a comprehensive solution that identifies and
removes comments prior to checking validity of the statement to ensure no SQLi
vectors occur.

The implementation of ORDER BY and GROUP BY in Zend_Db_Select of ZF1 is
vulnerable by the following SQL injection:

$db = Zend_Db::factory(/* options here */);
$select = new Zend_Db_Select($db);
$select->from('p');
$select->order("MD5("a(");DELETE FROM p2; #)"); // same with group()

The above $select will render the following SQL statement:

SELECT `p`.* FROM `p` ORDER BY MD5("a(");DELETE FROM p2; #) ASC

instead of the correct one:

SELECT "p".* FROM "p" ORDER BY "MD5(""a("");DELETE FROM p2; #)" ASC

This security fix can be considered an improvement of the previous
ZF2016-02 and
ZF2014-04 advisories.

As a final consideration, we recommend developers either never use user input
for these operations, or filter user input thoroughly prior to invoking
Zend_Db. You can use the Zend_Db_Select::quoteInto() method to filter the
input data, as shown in this example:

$db    = Zend_Db::factory(...);
$input = "MD5("a(");DELETE FROM p2; #)"; // user input can be an attack
$order = $db->quoteInto("SQL statement for ORDER", $input);

$select = new Zend_Db_Select($db);
$select->from('p');
$select->order($order); // same with group()

Action Taken

We fixed the reported SQL injection by removing comments from the SQL statement
before passing it to either the order() or group() methods; this patch
effectively solves any comment-based SQLi vectors.

We used the following regex to remove comments from a SQL statement:

const REGEX_SQL_COMMENTS = '@
    ((['"]).*?[^\]) # $1 : Skip single & double quoted expressions
    |(                   # $3 : Match comments
        (?:#|--).*?$    # - Single line comments
        |                # - Multi line (nested) comments
         /*             #   . comment open marker
            (?: [^/*]    #   . non comment-marker characters
                |/(?!*) #   . ! not a comment open
                |*(?!/) #   . ! not a comment close
                |(?R)    #   . recursive case
            )*           #   . repeat eventually
        */             #   . comment close marker
    )s*                 # Trim after comments
    |(?<=;)s+           # Trim after semi-colon
    @msx';

The patch is available starting in Zend Framework 1.12.20.

Other Information

This SQL injection attack does not affect Zend Framework 2 and 3 versions because the
implementations of ZendDbSqlSelect::order() and ZendDbSqlSelect::group() do
not manage parenthetical expressions.

Acknowledgments

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

  • Hiroshi Tokumaru (HASH Consulting Corp.), who discovered the issue;
  • Masanobu Katagi (Japan Computer Emergency Response Team Coordination Center),
    who reported the issue;
  • Enrico Zimuel, who provided the patch.

Source: Zend security feed

End-to-end encryption with Zend Framework 3

Recently, we released zend-crypt
3.1.0, the cryptographic component from Zend Framework.
This last version includes a hybrid cryptosystem,
a feature that can be used to implement end-to-end encryption
schema in PHP.

A hybrid cryptosystem is a cryptographic mechanism that uses symmetric encyption
(e.g. AES) to
encrypt a message, and public-key cryptography (e.g.
RSA) to protect the
encryption key. This methodology guarantee two advantages: the speed of a
symmetric algorithm and the security of public-key cryptography.

Before we talk about the PHP implementation, let’s explore the hybrid mechanism
in more detail. Below is a diagram demonstrating a hybrid encryption schema:

Encryption schema

A user (the sender) wants to send a protected message to another user
(the receiver). He/she generates a random session key (one-time pad) and uses
this key with a symmetric algorithm to encrypt the message (in the figure, Block
cipher
represents an authenticated encryption
algorithm). At the same time, the sender encrypts the session key using the
public key of the receiver. This operation is done using a public-key
algorithm, e.g., RSA. Once the encryption is done, the sender can send
the encrypted session key along with the encrypted message to the receiver.
The receiver can decrypt the session key using his/her private key, and
consequently decrypt the message.

This idea of combining together symmetric and asymmetric (public-key) encryption
can be used to implement end-to-end encryption (E2EE). An E2EE is a
communication system that encrypts messages exchanged by two users with the
property that only the two users can decrypt the message. End-to-end encryption
has become quite popular in the last years with the usage in popular software,
and particularly messaging systems, such as WhatsApp.
More generally, when you have a software used by many users, end-to-end
encryption can be used to protect information exchanged by users. Only the
users can access (decrypt) exchanged information; even the administrator of
the system is not able to access this data.

Build end-to-end encryption in PHP

We want to implement end-to-end encryption for a web application with user
authentication. We will use zend-crypt 3.1.0 to implement our cryptographic
schemas. This component of Zend Framework uses the OpenSSL extension
for PHP for its cryptographic primitives.

The first step is to create public and private keys for each users. Typically,
this step can be done when the user credentials are created. To generare the
pairs of keys, we can use ZendCryptPublicKeyRsaOptions. Below
is an example demonstrating how to generate public and private keys to
store in the filesystem:

use ZendCryptPublicKeyRsaOptions;
use ZendCryptBlockCipher;

$username = 'alice';
$password = 'test'; // user's password

// Generate public and private key
$rsaOptions = new RsaOptions();
$rsaOptions->generateKeys([
    'private_key_bits' => 2048
]);
$publicKey  = $rsaOptions->getPublicKey()->toString();
$privateKey = $rsaOptions->getPrivateKey()->toString();

// store the public key in a .pub file
file_put_contents($username . '.pub', $publicKey);

// encrypt and store the private key in a file
$blockCipher = BlockCipher::factory('openssl', array('algo' => 'aes'));
$blockCipher->setKey($password);
file_put_contents($username, $blockCipher->encrypt($privateKey));

In the above example, we generated a private key of 2048 bits. If you are wondering
why not 4096 bits, this is questionable and depends on the real use
case. For the majority of applications, 2048 is still a good key size, at least
until 2030. If you want more security and you don’t care about the additional
CPU time, you can increase the key size to 4096. We suggest reading the following
blog posts for more information on key key size:

We did not generate the private key using a passphrase; this is because the
OpenSSL extension of PHP does not support AEAD (Authenticated Encrypt with
Associated Data) mode yet; AEAD mode will be supported starting in
PHP 7.1, which should release this
autumn.

The default passphrase encryption algorithm for OpenSSL is
des-ede3-cbc using
PBKDF2 with 2048 iterations for
generating the encryption key from the user’s password. Even if this encryption
algorithm is quite good, the number of iterations of PBKDF2 is not optimal;
zend-crypt improves on this in a variety of ways, out-of-the-box. As
demonstrated above, we use ZendCryptBlockCipher to encrypt the private key;
this class provides encrypt-then-authenticate
using the AES-256 algorithm for encryption and HMAC-SHA-256 for
authentication. Moreover, BlockCipher uses the PBKDF2
algorithm to derivate the encryption key from the user’s key (password). The
default number of iterations for PBKDF2 is 5000, and you can increase it using
the BlockCipher::setKeyIteration() method.

In the example, we stored the public and private keys in two files named,
respectively, $username.pub and $username. Because the private file is encrypted,
using the user’s password, it can be access only by the user. This is a very
important aspect for the security of the entire system (we take for granted that
the web application stores the hashes of the user’s passwords using a secure
algorithm such as bcrypt).

Once we have the public and private keys for the users, we can start using the
hybrid cryptosystem provided by zend-crypt. For instance, imagine Alice
wants to send an encrypted message to Bob:

use ZendCryptHybrid;
use ZendCryptBlockCipher;

$sender   = 'alice';
$receiver = 'bob';
$password = 'test'; // bob's password

$msg = sprintf('A secret message from %s!', $sender);

// encrypt the message using the public key of the receiver
$publicKey  = file_get_contents($receiver . '.pub');
$hybrid     = new Hybrid();
$ciphertext = $hybrid->encrypt($msg, $publicKey);

// send the ciphertext to the receiver

// decrypt the private key of bob
$blockCipher = BlockCipher::factory('openssl', ['algo' => 'aes']);
$blockCipher->setKey($password);
$privateKey = $blockCipher->decrypt(file_get_contents($receiver));

$plaintext = $hybrid->decrypt($ciphertext, $privateKey);

printf("%sn", $msg === $plaintext ? "The message is: $msg" : 'Error!');

The above example demonstrates encrypting information between two users. Of
course, in this case, the sender (Alice) knows the message because she wrote
it. More in general, if we need to store a secret between multiple users, we need
to specify the public keys to be used for encryption.

The hybrid component of zend-crypt supports encrypting messages for multiple
recipients. To do so, pass an array of public keys in the $publicKey parameter
of ZendCryptHybrid::encrypt($data, $publicKey).

Below demonstrates encrypting a file for two users, Alice and Bob.

use ZendCryptHybrid;
use ZendCryptBlockCipher;

$data    = file_get_contents('path/to/file/to/protect');
$pubKeys = [
  'alice' => file_get_contents('alice.pub'),
  'bob'   => file_get_contents('bob.pub')
];

$hybrid     = new Hybrid();

// Encrypt using the public keys of both alice and bob
$ciphertext = $hybrid->encrypt($data, $pubKeys);

file_put_contents('file.enc', $ciphertext);

$blockCipher = BlockCipher::factory('openssl', ['algo' => 'aes']);

$passwords = [
  'alice' => 'password of Alice',
  'bob'   => 'password of Bob'
];

// decrypt using the private keys of alice and bob, one at time
foreach ($passwords as $id => $pass) {
  $blockCipher->setKey($pass);
  $privateKey = $blockCipher->decrypt(file_get_contents($id));
  $plaintext  = $hybrid->decrypt($ciphertext, $privateKey, null, $id);
  printf("%s for %sn", $data === $plaintext ? 'Decryption ok' : 'Error', $id);
}

For decryption, we used a hard coded password for the users. Usually, the user’s
password is provided during the login process of a web application and should
not be stored as permanent data; for instance, the user’s password can be saved
in a PHP session variable for temporary usage. If you use sessions to save
the user’s password, ensure that data is protected; the
PHP-Secure-Session library or
the Suhosin PHP extension will help you do so.

To decrypt the file we used the ZendCryptHybrid::decrypt() function,
where we specified the $privateKey, a null passphrase, and finally the $id
of the privateKey. This parameters are necessary to find the correct key to use
in the header of the encrypted message.

Source: Zend feed

ZF2016-02: Potential SQL injection in ORDER and GROUP statements of Zend_Db_Select

ZF2016-02: Potential SQL injection in ORDER and GROUP statements of Zend_Db_Select

The implementation of ORDER BY and GROUP BY in Zend_Db_Select of ZF1 is
vulnerable by the following SQL injection:

$db = Zend_Db::factory(/* options here */);
$select = new Zend_Db_Select($db);
$select->from('p');
$select->order("MD5("(");DELETE FROM p2; #)"); // same with group()

The above $select will render the following SQL statement:

SELECT `p`.* FROM `p` ORDER BY MD5("");DELETE FROM p2; #) ASC

instead of the correct one:

SELECT `p`.* FROM `p` ORDER BY "MD5("""");DELETE FROM p2; #)" ASC

This security fix can be considered as an improvement of the previous
ZF2014-04.

Action Taken

We fixed the reported SQL injection using two regular expressions for the order() and the group()
methods in Zend_Db_Select, created as the class constants REGEX_COLUMN_EXPR_ORDER and
REGEX_COLUMN_EXPR_GROUP, respectively. These are defined as:

/^([w]+s*(([^()]|(?1))*))$/

This regexp is different from the previous REGEX_COLUMN_EXPR, which used the
character patterm [w]*; we now require at least one word boundary ([w]+).

The patch is available starting in Zend Framework 1.12.19.

Other Information

This SQL injection attack does not affect Zend Framework 2 and 3 versions because the
implementations of ZendDbSqlSelect::order() and ZendDbSqlSelect::group() do
not manage parenthetical expressions.

Acknowledgments

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

  • Peter O’Callaghan, who discovered and reported the issue;
  • Enrico Zimuel, who provided the patch.

Source: Zend security feed

1 2 3 4