How To Automatically Scan Uploaded Files For Viruses With php-clamavlib - Page 2

Want to support HowtoForge? Become a subscriber!
 
Submitted by falko (Contact Author) (Forums) on Thu, 2007-01-11 17:09. ::

5 A Small Example

We will now create a small upload script, /var/www/upload.php, which contains an HTML upload form. If you submit the form, the script will call itself and use cl_info(), cl_scanfile(), cl_setlimits(), and clam_get_version() to scan the uploaded file for viruses. If the file is ok, it will be uploaded to /var/www/uploads, otherwise the script will display an error message (saying which virus/worm etc. it found) and delete the file on the server.

First, we must create the directory /var/www/uploads and make it writable by our Apache user, www-data:

mkdir /var/www/uploads
chown www-data:www-data /var/www/uploads

Then we create the file /var/www/upload.php:

vi /var/www/upload.php

<?php
$upload_dir = '/var/www/uploads/';

if($_POST){
  $error = '';
  //print_r($_FILES);
  if($_FILES['file']['size'] == 0 || !is_file($_FILES['file']['tmp_name'])){
     $error .= 'Please select a file for upload!';
  } else {
    cl_setlimits(5, 1000, 200, 0, 10485760);
    if($malware = cl_scanfile($_FILES['file']['tmp_name'])) $error .= 'We have Malware: '.$malware.'<br>ClamAV version: '.clam_get_version();
  }
  if($error == ''){
    rename($_FILES['file']['tmp_name'], $upload_dir.$_FILES['file']['name']);
  }
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>File-Upload</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>

<body>
<form method="post" action="upload.php" name="fileupload" enctype="multipart/form-data">
<table width="100%" border="0" cellspacing="0" cellpadding="2">
  <tr><td><b>File Upload</b></td></tr>
  <tr><td> </td></tr>
  <?php
  if(isset($error)){
    if($error != ''){
  ?>
  <tr><td><?php echo cl_info(); ?></tr>
  <tr><td><b>Error:</b> <?php echo $error; ?></td></tr>
  <?php
    } else {
  ?>
  <tr><td><b>File <?php echo $_FILES['file']['name']; ?> has successfully been uploaded to <?php echo $upload_dir; ?>!</b></td></tr>
  <?php
    }
  }
  ?>
  <tr><td>
    <table width="500" border="0" cellspacing="0" cellpadding="2">
      <tr>
        <td width="126">File:</td>
        <td width="366"><input type="file" name="file" size="30" value="" maxlength="255"></td>
      </tr>
      <tr>
        <td> </td>
        <td><input name="Upload" type="submit" value="Upload"> <input name="Cancel" type="reset" value="Cancel"></td>
      </tr>
    </table>
  </td></tr>
</table>
</form>
</body>
</html>

It contains the form for uploading a file:

[...]
<form method="post" action="upload.php" name="fileupload" enctype="multipart/form-data">
<table width="100%" border="0" cellspacing="0" cellpadding="2">
  <tr><td><b>File Upload</b></td></tr>
  <tr><td> </td></tr>
  <?php
  if(isset($error)){
    if($error != ''){
  ?>
  <tr><td><?php echo cl_info(); ?></tr>
  <tr><td><b>Error:</b> <?php echo $error; ?></td></tr>
  <?php
    } else {
  ?>
  <tr><td><b>File <?php echo $_FILES['file']['name']; ?> has successfully been uploaded to <?php echo $upload_dir; ?>!</b></td></tr>
  <?php
    }
  }
  ?>
  <tr><td>
    <table width="500" border="0" cellspacing="0" cellpadding="2">
      <tr>
        <td width="126">File:</td>
        <td width="366"><input type="file" name="file" size="30" value="" maxlength="255"></td>
      </tr>
      <tr>
        <td> </td>
        <td><input name="Upload" type="submit" value="Upload"> <input name="Cancel" type="reset" value="Cancel"></td>
      </tr>
    </table>
  </td></tr>
</table>
</form>
[...]

After you've selected a file and clicked on the Submit button, the script will call itself (action="upload.php") and execute the PHP code at the top:

[...]
if($_POST){
  $error = '';
  //print_r($_FILES);
  if($_FILES['file']['size'] == 0 || !is_file($_FILES['file']['tmp_name'])){
     $error .= 'Please select a file for upload!';
  } else {
    cl_setlimits(5, 1000, 200, 0, 10485760);
    if($malware = cl_scanfile($_FILES['file']['tmp_name'])) $error .= 'We have Malware: '.$malware.'<br>ClamAV version: '.clam_get_version();
  }
  if($error == ''){
    rename($_FILES['file']['tmp_name'], $upload_dir.$_FILES['file']['name']);
  }
}
[...]

Details about the uploaded file are saved in the $_FILES array, so we use that to check the file (you can uncomment the print_r($_FILES); line to see what is stored in the array).

I use the cl_setlimits() function to set limits for the virus scanning process in order to prevent DOS attacks (where the virus scanning process could eat up all resources on the system). The usage is like this:

cl_setlimits($maxreclevel, $maxfiles, $maxratio, $archivememlim, $maxfilesize)

  • $maxreclevel: integer value /* maximal recursion level */
  • $maxfiles: integer value /* maximal number of files to be scanned within archive */
  • $maxratio: integer value /* maximal compression ratio */
  • $archivememlim: boolean /* limit memory usage for bzip2 (0/1) */
  • $maxfilesize: long integer /* archived files larger than this value (in bytes) will not be scanned */

Basically, these values define the behaviour of ClamAV if archives (zip files, tar.gz files, bz2 files, etc.) are scanned. If you don't use the cl_setlimits() function, the respective values from the php.ini are taken.

The main function is the cl_scanfile() function which takes the path to the file to scan as argument. Uploaded files are temporarily saved (usually in /tmp; depends on your php.ini settings) before they are processed. That temporary file is saved in $_FILES['file']['tmp_name'] so we pass that variable to the cl_scanfile() function. If no virus is found, it gives back FALSE, otherwise the name of the virus it found.

clam_get_version() doesn't take any argument. That function gives back the version of the installed ClamAV (like 0.88.4).

Finally I use the function cl_info() which - like clam_get_version() - doesn't take any argument. It displays more detailed information about ClamAV, for example ClamAV version 0.88.4 with 85917 virus signatures loaded.

Now let's test. Type in http://192.168.0.100/upload.php in your browser. You should see this:

Click on Browse... and select a file to upload from your hard disk (a file that is no virus and not bigger than 2MB - that's the default maximum upload size which again can be changed in your php.ini):

The upload should succeed, and you should see something like this:

Now we need to get a virus to test. Fortunately there's the Eicar test virus, it's a file that doesn't do any harm, but its signature was included in all virus scanners out there so that you can test if your virus scanner is working by using that test virus. Go to http://www.eicar.org/anti_virus_test_file.htm and download the eicar.com, the eicar_com.zip, and the eicarcom2.zip files to your hard disk. Then test the upload with each of them:

If all goes well, php-clamavlib should recognize the virus and refuse the upload:

 

6 Links


Please do not use the comment function to ask for help! If you need help, please use our forum.
Comments will be published after administrator approval.
Submitted by Daniel15 (registered user) on Wed, 2007-01-17 11:55.
It looks like this module originally had a website (phpclamavlib.org), but it's since been taken offline. It appears as if this module is no longer maintained (it was removed from freshmeat.net, and the official website is no longer active) A PEAR package that's still maintained is available from http://trickie.org/code/phplibclamav.php