alirdn/os-project/SecureUPload Documentation

Installation


Server Requirements

  • PHP >= 5.3.0

Installing SecureUPload

SecureUPload is a composer package available on Packagist (alirdn/secureupload). So it can be installed easily using composer. $ composer require alirdn/secureupload But if you don’t have composer, or any other reason, SecureUPload package also include an PSR-4 autoloader. After Download and extracting package archive file, package autoloader should be included before where you want to use SecureUPload.
<?php
require_once "src/autoloader.php"

Core concepts


Every file uploads have four phases and each  phases have security steps.
  1. Before submit the form: This phase is when user want to see input form. This phase Security steps are:
    • Check that user has privilege to submit the form.
    • Check for DDOS attack by mass submitting form with small files.
  2. Submit the form: This phase is when a user submit a form, and file has been uploaded to temporary folder. This phase Security steps are:
    • Check that the files are really uploaded and file tmp_name is not set by attacker.
    • Check file minimum or maximum size.
    • Check file extension is not in forbidden extensions (like .php, .html etc).
    • Check file extension is acceptable. For example for images check extension is jpg, jpeg, png or gif.
    • Check file mime type is acceptable.
  3. Store: This phase is when an upload file passed checks, and now must be stored in uploads folder. This phase Security steps are:
    • If upload folder is in web root (Some where that can be accessed by URL), secure it by .htaccess file.
    • Move uploaded file securely.
  4. Access the stored file uploads: This phase is when user needs that file. This phase Security check are:
    • If files stored out of web root or in web root without giving user direct access, give the file to the user, securely.
    • If files stored in web root with direct access, check that only accepted file types is allowed to access and any forbidden file extension should be forbidden to access or executed.
Currently SecureUPload package will take care of three of four phases (Phase 2, 3 and 4). Maybe in future phase 1 checks will be included. Anyway, SecureUPload gives functionality to check uploaded file, store and access it securely. Moreover, SecureUPload will secure upload folder in web root by .htaccess file. All the functionality could be accessed by creating an object from Alirdn\SecureUPload\SecureUPload class. Before creating this object, you should create another object from Alirdn\SecureUPload\Config\SecureUPloadConfig, to configure SecureUPload object, and give it to  SecureUPload object. Besides upload files configs in SecureUPloadConfig (file_types, min_filesize, max_filesize), for each upload file fields in form, you can config it by give an object of Alirdn\SecureUPload\Config\UploadConfig.

Config\Config


Alirdn\SecureUPload\Config\SecureUPloadConfig and Alirdn\SecureUPload\Config\UploadConfig both extends Alirdn\SecureUPload\Config\Config class.

Alirdn\SecureUPload\Config\Config Methods:

__construct($config_array)

This class __constructor accepts an array contains key=>value indexes and save given config array.
<?php
$Config = new Alirdn\SecureUPload\Config\Config(
array(
'config_id' => 'config_value',
'config_another_id' => 0
)
);
echo $Config->get('config_id'); // echo config_value

set($config_id, $config_value)

Set a config index with it’s id and value.
<?php
$Config = new Alirdn\SecureUPload\Config\Config;
$Config->set('config_id','config_value');

setArray($config_array)

Set an array as config.
<?php
$Config = new Alirdn\SecureUPload\Config\Config();
$Config->setArray(
array(
'config_id' => 'config_value',
'config_another_id' => 0
)
);

get($config_id)

Get a config index by it’s id.
<?php
$Config = new Alirdn\SecureUPload\Config\Config();
$Config->setArray(
array(
'config_id' => 'config_value',
'config_another_id' => 0
)
);
echo $Config->get('config_id'); // echo config_value
echo $Config->get('config_another_id'); // echo 0
echo $Config->get('config_non_exist_id'); // echo (empty string)

printAll()

Just pretty print config.
<?php
$Config = new Alirdn\SecureUPload\Config\Config();
$Config->setArray(
array(
'config_id' => 'config_value',
'config_another_id' => 0
)
);
$Config->printAll();
/*
Output:
<pre>
Array
(
[config_id] => config_value
[config_another_id] => 0
)
</pre>
*/

Notes:

setting config could be chained.
<?php
$Config = new Alirdn\SecureUPload\Config\Config();
$Config
->setArray(
Array
(
[config_id] => config_value
[config_another_id] => 0
)
)
->set('final_config_id', 'final_value');
echo $Config->get('config_id'); // echo config_value
echo $Config->get('config_another_id'); // echo 0
echo $Config->get('config_non_exist_id'); // echo (empty string)
echo $Config->get('final_config_id'); // echo final_value

Config\SecureUPloadConfig


SecureUPloadConfig is used to configure how you want that SecureUPload behave. This config has below fields:
  • upload_folder: Path to the upload folder that uploaded files will be stored. Default value: '' type: string
  • storage_type: ِetermine how the files will be stored and accessed. This field give the SecureUpload information about where is the uploaded files folder located and how you will access to uploaded file. Default value: 1 type: numeric Values:
    • 1: Store somewhere in web root with direct access
    • 2: Store somewhere in web root without direct access
    • 3: Store somewhere out of web root
  • file_types: array of acceptable file extension as key and file extension mime type as value. Default value: array('jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif') type: array
  • organize_by: Determine how uploaded files should be organize. It means creating nested folders according to organization method to store different uploaded file. Default value: none type: string Values:
    • none: No organization. Store all uploaded files in upload_folder root.
    • type: Organize by type(extension). Move Uploaded files to sub folder of upload_folder root by it’s extension. Example: 01.jpg => upload_folder\jpeg\01.jpg
    • date: Organize by date of upload. Move Uploaded files to sub folder of upload_folder root by it’s date. Example: 01.jpg (Uploaded in 2016 january 1sth)=> upload_folder\16\02\01\01.jpg
    • typeThenDate: Organize by type(extension) then date of upload. Move Uploaded files to sub folder of upload_folder root by it’s extension then date. Example: 01.jpg (Uploaded in 2016 january 1sth)=> upload_folder\jpg\16\02\01\01.jpg
    • dateThenType: Organize by date then type(ext) of upload. Move Uploaded files to sub folder of upload_folder root by it’s date then extension. Example: 01.jpg (Uploaded in 2016 january 1sth)=> upload_folder\16\02\01\jpg\01.jpg
  • min_filesize: Minimum accepted file size in bytes. Default value: 0 type: numeric
  • max_filesize: Maximum accepted file size in bytes. 0 means no limitation in maximum file size. Note: PHP configs in php.ini limitation will not affected by this config. Configs that limit maximum file size are post_max_size, upload_max_filesize and memory_limit. Default value: 0 type: numeric

Config\UploadConfig


This config is for times when your form have more than one file filed and these fields accept different file types or different minimum/maximum allowed file sizes. For example, imagine that your form accepts image files (jpg, jpeg, png and gif) plus zip archive file. And your form have two file fields, one for an image (name is attachment_image) and one for an zip archive (name is attachment_file). You want that images maximum file size to be 1Mb, and for zip 8Mb. By using just SecureUPloadConfig you can’t accomplish this task. This task could be solved by using UploadConfig for each form fields. This config has below fields:
  • file_types: array of acceptable file extension as key and file extension mime type as value. Default value: array('jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif') type: array
  • min_filesize: Minimum accepted file size in bytes. Default value: 0 type: numeric
  • max_filesize: Maximum accepted file size in bytes. 0 means no limitation in maximum file size. Note: PHP configs in php.ini limitation will not affected by this config. Configs that limit maximum file size are post_max_sizeupload_max_filesize and memory_limit. Default value: 0 type: numeric

SecureUPload


All the SecureUPload package functionality are in this class. So you should create an object from this class and call it’s methods for different operations. You should create an object from Alirdn\SecureUPload\Config\SecureUPloadConfig and give it as the first argument when creating a new object from it. In Alirdn\SecureUPload\Config\SecureUPloadConfig object, at least you should set the upload_folder config before giving it to the SecureUPload constructor function. Other config indexes will have default values. But with empty upload_folder config index, SecureUpload will throw an exception of Alirdn\SecureUPload\Exceptions\UploadFolderException with code 1. Also, if path of upload_folder doesn’t exist,  SecureUpload will throw an exception of Alirdn\SecureUPload\Exceptions\UploadFolderException with code 2.

Alirdn\SecureUPload\SecureUPload Methods:

__construct(SecureUPloadConfig $SecureUPloadConfig, $initialize_upload_folder = true)

When creating new object, an object of Alirdn\SecureUPload\Config\SecureUPloadConfig must give to it. The second argument is for bypassing upload folder initialization. For more info about upload folder initialization see UploadFolder section.
<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
// Bypass Upload folder initialization
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig, false);

uploadFile($id, UploadConfig $UploadConfig = null)

Use to upload single file. $id  is input field name, so index id in PHP $_FILES magic global variable. This method will get all the needed information from $_FILES variable. Second argument is for upload specific config (more info: UploadConfig section). This method return an object of Alirdn\SecureUPload\Upload\Upload. So developer should check returned Upload object status for error checking or saving the id for future uses. More information about Upload object in Upload\Upload section.
<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$Upload = $SecureUPload->uploadFile('attachment');
/* Check that file uploaded or not. Then do corresponding action. */
if ( $Upload->status ) {
// File has been set in <input type="file" name="file"/>
if ( $Upload->status == 1 ) {
echo 'File uploaded successfully. Id: ' . $Upload->id;
} else {
echo 'File didn\'t uploaded. Error code: ' . $Upload->error;
}
} else {
// No file is selected in input field
}

uploadFiles($id, UploadConfig $UploadConfig = null)

Use to upload multiple files. $id  is input fields name, so index id in PHP $_FILES magic global variable. In html form multiple files could be used in two ways. First is by adding [] to the end of name of multiple file fields name. Second is by adding multiple attribute to a single file input.
<html>
<form>
<!-- First !-->
<input type="file" name="images[]" />
<input type="file" name="images[]" />
<input type="file" name="images[]" />
<!-- Second !-->
<input type="file" name="images[]" multiple/>
<!-- For each of above example you should call $SecureUPload->uploadFiles('images'); !-->
</form>
</html>
This method will get all the needed information from $_FILES variable and normalize it so working with them become so much easier. Second argument is for upload specific config (more info: UploadConfig section). This method return array. If no files is selected, and empty array will be returned. Otherwise, returned array contains Alirdn\SecureUPload\Upload\Upload objects that should be iterated by developer for errors or saving successful uploads id for future used.
<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$uploads = $SecureUPload->uploadFiles('images');
/* Check that file uploaded or not. Then do corresponding action. */
if(!empty($uploads)) {
// Files for uploading have been selected
foreach($uploads as $Upload) {
if ( $Upload->status == 1 ) {
echo 'File uploaded successfully. Id: ' . $Upload->id;
} else {
echo 'File didn\'t uploaded. Error code: ' . $Upload->error;
}
}
} else {
// No files is selected in input fields
}

getUpload($id)

Get uploaded file that has been uploaded and stored before by it’s id. This method returns Alirdn\SecureUPload\Upload\Upload object. If file exist, status is 1, if not status is 2 with 18 error code.
<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$Upload = $SecureUPload->getUpload('ca259cef4b42be49f7548e31aca10a29_png');
if($Upload->status == 1) {
// Upload is is correct and file exist
echo 'Uploaded file id is correct and file exist. Id: ' . $Upload->id . ' Path:' . $Upload->path;
} else {
// Given id is incorrect or file doesn't exist.
}

getUploadAsFile($id)

Use this method when uploads storing type is 2  or 3. So there is no direct access with URL to uploads. So this method will get an id, then if file exist and id is correct, it will give uploaded file content as a file with appropriate HTTP headers. If file doesn’t exist, an 404 HTTP header will be sent.
<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$SecureUPload->getUploadAsFile('ca259cef4b42be49f7548e31aca10a29_png');
exit;

removeUpload($id)

Remove previously uploaded and stored file using it’s id. This method return Boolean true/false. True for when given id is correct and file exist and removing it from file system is successful. False for opposite.
<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$upload_removed = $SecureUPload->removeUpload('ca259cef4b42be49f7548e31aca10a29_png');
if($upload_removed) {
// Uploaded and stored file removed successfully.
} else {
// Even id is not correct, file dosent exist or removing from file system failed.
}

Upload\Upload


This object has no methods. It’s only have public properties to get information about uploaded file or stored upload file.

Alirdn\SecureUPload\Upload\Upload Properties:

$status Upload status stored here. Default value: 0 Values:
  • 0: No file uploaded. When tmp_info[error] == UPLOAD_ERR_NO_FILE
  • 1: File uploaded and moved to upload_folder successfully
  • 2: File uploaded in PHP tmp_folder but and error occurred. See error_code property
$error Upload error code. Error codes separated into two parts. Codes between [0-9]. These codes are PHP’s upload error codes. Codes between [10-18]. These errors are new codes introduced by SecureUPload. Default value: 0 Values:
  • 0: There is no error. Uploaded uploaded and moved to upload folder successfully.
  • 1: The uploaded file exceeds the upload_max_filesize directive in php.ini.
  • 2: The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
  • 3: The uploaded file was only partially uploaded.
  • 4: No file was uploaded.
  • 5: ———————
  • 6: Missing a temporary folder. Introduced in PHP 5.0.3.
  • 7: Failed to write file to disk. Introduced in PHP 5.1.0.
  • 8: A PHP extension stopped the file upload.
  • 9: **** Reserved for PHP ***
  • 10: *** Reserved for SecureUPload ***
  • 11: File was not uploaded via HTTP POST.
  • 12: File size is less than minimum acceptable file size.
  • 13: File size is more than maximum acceptable file size.
  • 14: File extension is in forbidden file extensions.
  • 15: File extension is not in acceptable extensions.
  • 16: File mime type is not in acceptable files mime types.
  • 17: Error occurred during moving uploaded file to uploads folder.
  • 18: Get missing upload with id.
$name Upload new name used to store it. Default value: '' $ext Upload extension. Default value: '' $id Upload id. This id is what developer should save for future uses. Default value: '' $relative_path Upload relative path. Default value: '' $relative_url Upload relative URL. Default value: '' $path Upload full path. Default value: '' $size Upload file size in bytes. Default value: '' $type Upload mime type. Default value: ''

Usage instruction


Upload single file

<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$Upload = $SecureUPload->uploadFile('attachment');
/* Check that file uploaded or not. Then do corresponding action. */
if ( $Upload->status ) {
// File has been set in <input type="file" name="file"/>
if ( $Upload->status == 1 ) {
// Save id to database
} else {
// Show corresponding error.
}
} else {
// If file is neccessary show error, if not, no action needed.
}

Upload multiple files

<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$uploads = $SecureUPload->uploadFiles('images');
/* Check that file uploaded or not. Then do corresponding action. */
if(!empty($uploads)) {
// Files for uploading have been selected
foreach($uploads as $Upload) {
if ( $Upload->status == 1 ) {
// Save id to database
} else {
// Show corresponding error.
}
}
} else {
// If file is neccessary show error, if not, no action needed.
}

Get Uploaded file by id as Upload\Upload

<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$Upload = $SecureUPload->getUpload('ca259cef4b42be49f7548e31aca10a29_png');
if($Upload->status == 1) {
// Upload is is correct and file exist
// Echo $Upload->relative_url in image src etc...
} else {
// Given id is incorrect or file doesn't exist. So show an error or 404 not found
}

Get Uploaded file by id as file

<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$SecureUPload->getUploadAsFile('ca259cef4b42be49f7548e31aca10a29_png');
exit;

Remove Uploaded file by id

<?php
$SecureUPloadConfig = new Alirdn\SecureUPload\Config\Config;
$SecureUPloadConfig->set('upload_folder', 'uploads' . DIRECTORY_SEPARATOR);
// Initialize Upload folder
$SecureUPload = new Alirdn\SecureUPload\SecureUPload($SecureUPloadConfig);
$upload_removed = $SecureUPload->removeUpload('ca259cef4b42be49f7548e31aca10a29_png');
if($upload_removed) {
// Uploaded and stored file removed successfully.
} else {
// Even id is not correct, file dosent exist or removing from file system failed.
}