Key Takeaways
- ClamAV can be used as a validation filter in the Zend Framework to ensure that uploaded files are virus-free. This involves writing a new file validation filter that uses ClamAV.
- Installation of ClamAV support is required, with procedures based around Linux. Once installed, a new file upload validator can be built within the Zend Framework using the ClamAv library, which includes functions for scanning uploaded files and reporting any detected viruses.
- The validator can be tested through a form that utilizes it. If the form doesn’t validate after being submitted (i.e., the file contains a virus), a validation message will be displayed. Otherwise, a success message will be displayed to the user. This ensures virus-free uploads in applications created with the Zend Framework.
Adding ClamAV Support to PHP
First you’ll need to install ClamAV support. I’m basing this installation procedure around Linux, specifically Ubuntu. If you’re using another distribution, you may need to adjust the commands accordingly. Unfortunately, if you’re using Windows however, you’ll need to use a Linux-based Virtual Appliance or setup a virtual machine running Linux to follow along since the php-clamav extension doesn’t support Windows as yet. Before you attempt to install ClamAv, ensure that you have the library’s dependencies installed. You’ll also want to make sure you have the PHP dev package installed sophpize
is available. You can do this by running the following command:
msetter@tango:~$ sudo apt-get install php5-dev libclamav-dev clamav libclamav6 clamav-freshclamOnce you have the dependencies installed, grab a copy of the php-clamav library from sourceforge.net/projects/php-clamav and extract it to a temporary directory on your system. Navigate into the extracted library’s directory and run the following commands:
msetter@tango:~/php-clamav$ phpize msetter@tango:~/php-clamav$ ./configure --with-clamav msetter@tango:~/php-clamav$ makeIf they all execute without errors, you’ll find a newly compiled module in the modules subdirectory. Copy the module to the directory in which the rest of your PHP modules reside. Your system may vary, but I was able to do it with:
msetter@tango:~/php-clamav$ sudo cp modules/clamav.so /usr/lib/php5/20090626+lfs/You then need to enable the module in PHP’s configuration file. This is done pretty simply by adding the following line to
php.ini
and restarting Apache:
extension=clamav.soFinally, either run
php -i
from the command line or execute a simple PHP script that contains just a call to phpinfo()
to verify the new extension is enabled. You should see output similar to that below.
The ClamAv library comes with a series of constants and functions, but in this article I will focus on just two functions, cl_scanfile()
and cl_pretcode()
, as all you need to do is scan the uploaded file and report what the virus is if one is found. For more information on the other available functions visit php-clamav.sourceforge.net.
Building the File Upload Validator
Now that the extension is installed and enabled, let’s get underway and build the Zend Framework ClamAV file upload validator. I’ll assume that you already have a working Zend Framework project which has module support enabled and ready to go. Add support for the new validation library by adding the following line to yourapplication.ini
file:
autoloaderNamespaces[] = "Common_"Then, under the library directory of your Zend Framework project root, create the directory
Common/Validate/File
and within it a file named ClamAv.php
with the following content:
<?php
class Common_Validate_File_ClamAv extends Zend_Validate_Abstract
{
}
With that, your new validator class will be available to the project.
If you’re not familiar with validators in Zend Framework, they’re a pretty straight-forward affair. You can either extend them from Zend_Validate_Abstract
or Zend_Validate_Interface
. For the purposes of this example, I’m basing the validator on the former. Given that, you will only have to implement two methods: the constructor and isValid()
.
The constructor should check whether the ClamAv extension is loaded as it’s not shipped with a standard distribution of PHP.
The isValid()
method will perform the core work of the validator. Normally the method validates some input and either returns true if the validation was successful or sets an error message in the errors list that is shown afterwards and returns false if the validation failed. Depending on the configuration of your form validators, returning false will either halt the form validation at that point or let the remaining validators continue to run.
Fill out the Common_Validate_File_ClamAv
class so it looks like this:
<?php
class Common_Validate_File_ClamAv extends Zend_Validate_Abstract
{
const STATUS_CLEAN = 0;
const NOT_READABLE = "fileNotReadable";
const FILE_INFECTED = "fileInfected";
protected $_messageTemplates = array(
self::FILE_INFECTED => "File '%value%' is infected",
self::NOT_READABLE => "File '%value%' is not readable");
public function __construct() {
if (!extension_loaded('clamav')) {
throw new Zend_Validate_Exception(
"ClamAv extension is not loaded");
}
}
public function isValid($value, $file = null) {
if ($file === null) {
$file = array("type" => null, "name" => $value);
}
if (!Zend_Loader::isReadable($value)) {
return $this->_throw($file, self::NOT_READABLE);
}
$retcode = cl_scanfile($value, $virusname);
if ($retcode !== self::STATUS_CLEAN) {
printf("File path: %s | Return code: %s | Virus found name: %s",
$value, cl_pretcode($retcode), $virusname);
return $this->_throw($file, self::FILE_INFECTED);
}
return true;
}
protected function _throw($file, $errorType) {
$this->_value = $file["name"];
$this->_error($errorType);
return false;
}
}
First a set of class constants are specified that define the return status for the virus check string templates for custom errors messages. Following that, the constructor checks for ClamAv support being available. If it’s not available, then an exception is thrown.
The isValid()
method checks if it the incoming $value
argument contains a filename and that the file is readable. If it is, then the cl_scanfile()
function is called. The return code from cl_scanfile()
indicates whether the file is virus-free. If not, then the name of the virus is retrieved using the cl_pretcode()
function and the information is printed.
The _throw()
method takes care of setting the appropriate error constant in the class and returning false to indicate that validation has failed. If this happens, the error message linked to the constant will be displayed in the upload form through the use of an error decorator on the input element.
Testing the Validator
With the validator written, you’ll need a form to make use of it and test that it works. Either manually or withzf.sh
, create a new action in the IndexController
class of the default module and call it “fileUpload”. Add the following code to it:
<?php
class IndexController extends Zend_Controller_Action
{
...
public function fileUploadAction() {
$form = new Zend_Form();
$form->setAction("/default/index/file-upload")
->setMethod("post");
$uploadFile = new Zend_Form_Element_File("uploadfile");
$uploadFile->addValidator(new Common_Validate_File_ClamAv())
->setRequired(true)
->setLabel("Upload file:");
$form->addElement($uploadFile);
$form->addElement(new Zend_Form_Element_Submit("submit"));
if ($form->isValid($_POST)) {
$values = $form->getValues();
$this->view->messages = array("File uploaded");
}
$this->view->form = $form;
}
}
Here you’ve created a simple form and set its action and method properties, a submit button, and a file element. The newly created ClamAv file validator is added to the file element. In addition, the required flag Is set to true ensuring that a file must be uploaded. Following this, both elements are added to the form and a simple if statement checks whether the form has been submitted.
If the form doesn’t validate after being submitted (i.e. the file has a virus), then a validation message will be displayed using the standard error message decorator. Otherwise, a message is added to the view’s messages which will be displayed to the user to indicate the upload was successful.
The last piece is the view script, which is shown below:
<h1>Zend Framework - ClamAV File Upload Validator</h1>
<?php
if (count($this->messages)) {
echo '<ul id="messages">';
foreach ($this->messages as $message) {
echo "<li>" . $this->escape($message) . "</li>";
}
echo "</ul>";
}
echo $this->form;
As the lions share of the work already taken care of by the controller and the validator, the view script doesn’t need to do a lot. It simply displays any messages that have been set by the controller and renders the form.
Summary
After working through all that code, you now have a new validator for the Zend Framework that, via the PHP ClamAv library, will check if a file is virus free. I hope that you found this article helpful, both for showing how to create your own custom validators in the Zend Framework and for being able to ensure that you have virus free uploads in the applications that you create from here on in. If you’d like to inspect the code further, code for this article is available for cloning on GitHub. Image via mathagraphics / ShutterstockMatthew Setter
View AuthorMatthew Setter is a software developer, specialising in reliable, tested, and secure PHP code. He’s also the author of Mezzio Essentials (https://mezzioessentials.com) a comprehensive introduction to developing applications with PHP's Mezzio Framework.
Advanced
Share this article