Discussion

Zend_File_Transfer examples or using validators to increase security

Hy interested ones,

the new Zend_File_Transfer component is growing day for day.
As incredible new feature this component allows to use file validators.

These are necessary to increase security and allow to define rules for file uploads (and also downloads in future). So let’s see some examples to get a feeling:

$upload = new Zend_File_Transfer_Adapter_Http();
$upload->addValidators('Size', '50kB)
          ->setDestination('C:/uploads')
          ->receive();

What we’ve done so far is to limit all sent files to 50kB filesize. Any file which is uploaded and exceeds the size of 50kB will throw an exception we can catch.

The more rules we define the more secure our upload will be.
So which other validators are supported until now:

  • Size: We already know this validator. He checks the filesize of single file. You can set a minimum and a maximum filesize.
  • Count: You should set this validator to represent exactly the amount of files you expect. He has also a mimimum and a maximum filecount. If this validator throws an error you are probably having an attack. But you can also limit the number of files to receive with this validator.
  • Extension: This validator checks for the extension of files. You can set multiple extensions to be checked. But remember that an evil user can manually change the extension so you should not rely only on the extension.
  • FilesSize: This validator also checks for the size of files. But different to the Size validator it checks for the size of ALL files. You could for example define that a single file must not exceed 50kB. But all files in sum must not exceed 200kB.
  • ImageSize: The ImageSize validator checks the size of given files when they are images. You can define a mimimum and a maximum image size for width and height.

So let’s see a full example of validators and a more secure upload:

$upload = new Zend_File_Transfer_Adapter_Http();
$upload->addValidators('Size', '250kB')
          ->addValidators('Count', 5)
          ->addValidators('FilesSize', '1MB')
          ->addValidators('Extension', 'gif, jpg, png')
          ->addValidators('ImageSize', array(10, 10, 1024, 768))
          ->setDestination('C:/uploads');
	
if (!$upload->isValid()) {
    print_r($upload->getMessages());
    die();
}
try {
    $upload->receive();
} catch (Zend_File_Transfer_Exception $e) {
    $e->getMessage();
}

So what we’ve created now is a fileupload for images.
Each imagefile can have 250kB maximum filesize. We allow in sum 5 images but all images im sum are not allowed to exceed 1MB. Additionally we allow gif, jpg and png files and define a imagesize of 10×10 up to 1024×768. All files are uploaded to ‘C:\uploads’.

As you see it’s not complicated to define a more secure upload then just using php’s move_uploaded_file.
Feel free to play around with this example.

If future there will be additional validators like MimeType and FileName.
Also filter will be added which allow you to change uploaded files on the fly before they are stored.
Filters could contain the automatic change of imagesize or changing textfiles to have a proper lineending and much more.

Greetings
Thomas, I18N Team Leader, Zend Framework

25 comments on Zend_File_Transfer examples or using validators to increase security

Ota on Monday, 21 July 2008 at 12:40:39 says:

Hey thomas,

seems nice and easy to use but how do you map the http file upload fields to zend file transfer?

thomasw on Monday, 21 July 2008 at 17:21:54 says:

This is done internally by Zend_File_Transfer at initialization. So Zend_File_Transfer has to be the target of your file upload form to work. This is because the temporary files will be deleted immediately after the next call of a php script (the target of your form).

You can use both syntax… single files or the multifile syntax. Both are recognised by Zend_File_Transfer.

Ota on Tuesday, 22 July 2008 at 02:50:03 says:

So its not possible to define what files should be handled/excluded by Zend_File_Transfer?

To make it more clear, as soon as i initiate ZFT HTTP Upload it takes “controll” of my HTTP uploads?

BTW: Do you have magic quotes gpc turned on? My quotes got escaped as i forgot to enter a name.

thomasw on Tuesday, 22 July 2008 at 10:38:53 says:

As you may already expect the API can much more than I’ve described. :-)

You can also handle single files… each method can get a files string or array. Define your files within there and only this files will be processed. Just keep in mind that after the executed script is finished ALL not received files are gone. But you could execute the receive method multiple times. Simplest way is just to give an array with the files.

Yes, when using the HTTP Adapter, it will automatically take you uploaded files without any further methodcall.

Related to matic_quotes_gpc… I’ve only tested with default settings of PHP. But keep in mind that it’s not possible to inject code within here. When a filename can not be found due to false characters like ” ‘ / and so on it will just return an validation error. So there is no security leak for file uploads.

Ota on Tuesday, 22 July 2008 at 17:43:44 says:

Its obvious that uploaded files are deleted by the php garbagecollector after the script finished executing.

I am just asking if it would theoretical be possible to handle one file via ZFT and another via the build in php file functions?

About the magic_quotes problem: I meant your blog ;) The quotes in comments get escaped when there is an user input error.

thomasw on Thursday, 24 July 2008 at 14:38:12 says:

It would theoretically be possible… but in my eyes completly useless. :-)

David on Thursday, 24 July 2008 at 15:18:49 says:

Did the Zend_File_Transfer component make it into the 1.6.0 RC1 ? I can’t seem to find it, but it’s mentioned in the release notes

thomasw on Thursday, 24 July 2008 at 16:48:03 says:

You can find it in the incubator. It will be officially released with RC2 because the necessary View Component was not finished as the release was made.

queefsniffer on Friday, 25 July 2008 at 01:05:58 says:

what about .jpeg extensions and the mime/type “image/pjpeg” that IE sends

queefsniffer on Friday, 25 July 2008 at 01:09:31 says:

I also remember a situation we ran into where we checked mime type instead of extension and there was a certain executable mime type that we didn’t every allow but then a new ms office format of some sort had it’s file uploaded with this same forbidden extension and all our clients got pissed because they could no longer upload excel or powerpoint or something kind of files.

What’s the best way around this problem?

thomasw on Friday, 25 July 2008 at 01:52:33 says:

What should be with such an extension ?
You can set any extension you need. The Extension validator validates only the file extensions you set. The MimeType validator will follow in a few days. It will allow to validate against mimetypes.

This situation is quite simple… why should a new software be supported by an old application ? This is the reason why customers should go for a support contract.
There is no way to automatically support things which are not known at creation of a component. This is a generic problem, not a file upload specific one.

Hugo on Friday, 25 July 2008 at 13:24:23 says:

Unfortunatelly, this component doesn’t work well. I explain. I’ve tried the code below :

<?php

$oFileTransfer = new Zend_File_Transfer();
$oFileTransfer->addValidators(’Size’,'50kB’)
->addValidators(’Extension’, ‘gif, jpg, png’)
->setDestination(dirname(__FILE__).’/uploads’);

?>

But I’ve the error that I can’t apply the addValidators() method on my object because this one isn’t a Zend_File_Transfer_Adapter_Abstract type. So I was forced to add a getAdapter() method to the Zend_File_Transfer class, which explicitly returns the $_adapter attribute.

So, it works with the code :

<?php

$oFileTransfer = new Zend_File_Transfer();
$oFileTransfer->getAdapter()
->addValidators(’Size’,'50kB’)
->addValidators(’Extension’, ‘gif, jpg, png’)
->setDestination(dirname(__FILE__).’/uploads’);

?>

Then there a big problem with the Zend_Validate_File_Extension validator, which doesn’t work because in you Zend_File_Transfer::isValid() method, you try to validate the extension as the same way as the other validators. All validators are applied on the $_FILES[’file’][’tmp_name’] value. But unfortunatelly, this value looks like something like that : /tmp/1235ShdeZe8

There is no extension for a temporary file, that’s why the extension validator provides an error (the warning that the index ‘extension’ doesn’t exist in the $infos array of the Zend_Validate_File_Extension::isValid() method).

So, you should apply the Zend_Validate_File_Extension::isValid() method on the $_FILES[’file’][’name’] value, which contains the “trully” name of the file stored in the client hard drive. This value contains the extension of the file and so can be checked.

I’m very impatient to discover the Zend_File_Transfer class in Zend Framework 1.2 because I really need it for my future projects.

I have two question for you Thomas. The first one is :

Will the Zend_File_Transfer component be coupled with the Zend_Form component ?

In fact, It would be great if we can have a Zend_Form_Element_File element that we can instanciate and add to the Zend_Form object. Then, we could instanciate a Zend_File_Transfer object and defined its differents parameters (validators, destination…). When this is done, we would associate this Zend_File_Transfer object to its Zend_Form_Element_File object.

So in the finish, it could be nice to have a code, which looks like this :

<?php
// File field
$oFileField = new Zend_Form_Element_File(’my_file’);
$oFileField->setLabel(’Your file’);

// Submit field
$oSubmitField = new Zend_Form_Element_Submit(’my_submit’);

// File transfer
$oFileTransfer = new Zend_File_Transfer();
$oFileTransfer->addValidators(’Size’,'50kB’)
->addValidators(’Extension’, ‘gif, jpg, png’)
->setDestination(dirname(__FILE__).’/uploads’);

// Associate the File Transfer object to its Element_File object
$oFileField->setFileTransferObject($oFileTransfer);

// Form
$oForm = new Zend_Form(’my/action’);
$oForm->setAttrib(’enctype’,'multipart/form-data’);
$oForm->addElement($oFileField);
$oForm->addElement($oSubmitField);

if(!empty($_POST) {
if($oForm->isValid($_POST, $_FILES)) {
// Checks, validates and receives in the fly the transmitted file
echo ‘Success’;
}
}
?>

My second question is :

Does the Zend_File_Transfer object accept filters ? Because it could be nice to add filters to the object in order to rename properly its name for example. If a user tried to upload a file named like this (french filename) : “dernière journée à l’ile Maurice.pdf”.

An associated filter could rename it on the fly in one of the formats below :

- derniere_journee_a_l_ile_maurice.pdf
- derniere-journee-a-l-ile-mauriece.pdf

Or simply with the sha1() or md5() encoded filename string. What do you think about my proposition?

Thanks for your work.

Hugo.

Hugo on Friday, 25 July 2008 at 14:06:26 says:

The Extension validator doesn’t work anymore. I can upload .exe files on my server even if I declared only jpg, png and gif as valid extensions…

thomasw on Friday, 25 July 2008 at 14:26:42 says:

Hugo: Use the adapter and not the base class… until now the base class is not in a finished state. See the example code above.

Related to your problems… please write issues you have within Jira. Others might also have such problems and will not find a solution when they do not read my blog… well they probably should ;-) Also this component is not finished for now… there is much in change.

As you may have noticed in the ZF lists, a form element will follow soon. But this is something which Matthew has raised his hand for, so I will not do it. He’s the Form-U-Nator :-)

Related to filters… when you look at the code you will recognise that there is a getFilters, setFilters and a addFilters method. They are not coded for now, as other things are more important. So they throw an exception. But they will come in future.

But filters are not meant to change filenames they are meant to change the content… for example automatically resize a image, change content encoding, revert line endings and so on… there are much ideas on this point.

Greetings
Thomas, I18N Team Leader, Zend Framework

Hugo on Friday, 25 July 2008 at 15:45:13 says:

Hello thomas,

Oops I didn’t see that I had to use the Zend_File_Transfer_Adapter_Http… I will do it now :)

Thank your answer. I hope ZF 1.6 RC 2 and stable will be soon unveiled. Actually I’m not using the ZF at work (only for my personnal projects) but some components separetly whose Zend_Form, Zend_View, Zend_Config, Zend_Loader, Zend_Registry or Zend_File.

Thanks.

Hugo.

thomasw on Friday, 25 July 2008 at 15:50:59 says:

No problem at all…

I’ve also added some minor changes, so you should update to the latest incubator version. The extension validator for example takes now also ‘name’ in account. So just get the latest version and keep on testing ;-)

Greetings
Thomas, I18N Team Leader, Zend Framework

Hugo on Friday, 25 July 2008 at 17:33:22 says:

I’m now registered at Jira issue tracker. I have started to report the bugs I’ve discovered by testing your component ;)

SayMyName on Monday, 4 August 2008 at 09:35:36 says:

Is it possible to give the extension validator an Array instead of an comma separeted string?

->addValidators(’Extension’, ‘gif, jpg, png’);

->addValidators(’Extension’, array(’gif’, ‘jpg’, ‘png’);

thomasw on Monday, 4 August 2008 at 10:18:03 says:

Of course is this syntax also supported. Just read the documentation within the incubator to see what’s possible.

Julien on Tuesday, 9 September 2008 at 15:18:01 says:

Hi,

Is it possible to upload several files from one single page/form? Could you please give a working example ?

Thanks in advance for your help;

Julien

thomasw on Tuesday, 9 September 2008 at 17:47:08 says:

Simply define several file elements in your form.
Zend_File_Translate (or the file element) will always handle all given files. There is no code to change for Zend_File_Transfer to support this.

For informations how to define multiple form elements, please look into the ZF manual.

jsnod on Sunday, 14 September 2008 at 02:56:53 says:

Hi, is there any way for the adapter to return the filenames of the successfully uploaded files? I suppose I could use $_FILES directly, but it would be much nicer to have the adapter return an array of files uploaded.

thomasw on Sunday, 14 September 2008 at 12:31:03 says:

Use the getFileName method which is described in the manual.

dawid on Monday, 22 September 2008 at 03:24:17 says:

Hi, great article:)
I have 2 questions:
1. Default desitnation of Zend_File_Transfer_Adapter_Http is tmp dir where files are uploaded but what about when users upload two difrent files with the same name? I look into code but i didnt find any solutions there.

2. What is better for sth like resizing images( ie. 2 resolutions), add watermark, or renaming(unique file name with id or in subdirectories) its better to write own ZF_Transfer_Adapter or writes filters for this actions? (i think more on philosphy doing this, more about approach)

thomasw on Monday, 22 September 2008 at 10:39:20 says:

1.) Simply use the Rename filter. Each file is targeted by the name of it’s form element.

2.) These are 2 different things. I am planning a ImageResize and a Watermark filter. But resizing image + adding a watermark changes content, and renaming only changes name and does not touch content. So it’s always a matter of what you want. Generally the component supports both ways so that writing a own adapter is not necessary.

Add comment

Fill out the form below to add your own comments

User data





Add your comment


Calendar

  • July 2008
    SunMonTueWedThuFriSat
     12345
    6789101112
    13141516171819
    20212223242526
    2728293031 

Last 8 comments

Certificates

zf-zce-logo.gif

zf-education-advisory-board-m.png

Admin area