File Upload and Media Handling

1. environment

php.ini

  • upload_max_filesize
  • post_max_size
  • max_input_time
  • max_execution_time
  • max_file_uploads
  • session.gc_maxlifetime

storage

  1. file system
    1. global
    2. perpage
  2. database

1.1. settings

options

  1. upload
    1. [0, 1, Admins] – refactor, last value is actually just the allowed user group
  2. upload_max_size
  3. upload_quota
  4. upload_quota_per_user
  5. upload_banned_exts
  6. upload_images_only

  1. check_mimetype
  2. img_create_thumbnail
  3. img_max_thumb_width

constants

  1. define('THUMB_DIR', 'file/thumb');
  2. define('UPLOAD_GLOBAL_DIR', 'file/global');
  3. define('UPLOAD_PER_PAGE_DIR', 'file/perpage');

roles


permissions

  • $this->has_access('upload')

Anti-Hack

Download Flood Minutes:
The number of minutes to set flood control to.

Download Flood Control:

The number of downloads allowed in period of 'Download Flood Minutes'.

1.2. chmod

Write access for files folders.


  1. User can read, write and execute
  2. Group can read and execute
  3. World can read and execute

Execute privilege on directories is required to access files inside them. (AT&T)


The important thing is that the webserver has write access.


Thus if the files directory is owned by the webserver user ( e.g. www-data ) 755 would suffice.
If the webserver is not owner, but is in the group owning the directory, 775 would be better.
777 is the extreme case where all users on the system have full access. In most cases this would not be advisable.

1.3. MIME type and file extensions

https://en.wikipedia.org/wiki/Media_type
https://mimesniff.spec.whatwg.org/


Blacklisting File Extensions


Whitelisting File Extensions

# Allowed uploadable file extensions and mimetypes

$extension_map	= array(
	// application
	'bz'		=> array('application', 'application/x-bzip'),
	'bz2'		=> array('application', 'application/x-bzip2'),
	'doc'		=> array('application', 'application/msword'),
	'exe'		=> array('application', 'application/octet-stream'),
	'latex'		=> array('application', 'application/x-latex'),
	'gtar'		=> array('application', 'application/x-gtar'),
	'gz'		=> array('application', 'application/x-gzip'),
	'gzip'		=> array('application', 'application/x-gzip'),
	'pdf'		=> array('application', 'application/pdf'),
	'ppt'		=> array('application', 'application/mspowerpoint'),
	'ps'		=> array('application', 'application/postscript'),
	'tar'		=> array('application', 'application/x-tar'),
	'tgz'		=> array('application', 'application/x-compressed'),
	'torrent'	=> array('application', 'application/x-bittorrent'),
	'xls'		=> array('application', 'application/excel'),
	'zip'		=> array('application', 'application/x-zip-compressed'),
	'7z'		=> array('application', 'application/x-7z-compressed'),

	'odc'		=> array('application', 'application/vnd.oasis.opendocument.chart'),
	'odb'		=> array('application', 'application/vnd.oasis.opendocument.database'),
	'odf'		=> array('application', 'application/vnd.oasis.opendocument.formula'),
	'odg'		=> array('application', 'application/vnd.oasis.opendocument.graphics'),
	'odi'		=> array('application', 'application/vnd.oasis.opendocument.image'),
	'odp'		=> array('application', 'application/vnd.oasis.opendocument.presentation'),
	'ods'		=> array('application', 'application/vnd.oasis.opendocument.spreadsheet'),
	'odt'		=> array('application', 'application/vnd.oasis.opendocument.text'),


	// (legacy)
	'pptx'		=> array('application', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'),
	'xlsx'		=> array('application', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
	'docx'		=> array('application', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'),

	// audio
	'm4a'		=> array('audio', 'audio/mp4'),
	'mp3'		=> array('audio', 'audio/x-mpeg-3'),
	'ogg'		=> array('audio', 'audio/ogg'),
	'opus'		=> array('audio', 'audio/ogg'),

	// image
	'gif'		=> array('image', 'image/gif'),
	'ico'		=> array('image', 'image/x-icon'),
	'jpe'		=> array('image', 'image/jpeg'),
	'jpeg'		=> array('image', 'image/jpeg'),
	'jpg'		=> array('image', 'image/jpeg'),
	'png'		=> array('image', 'image/png'),
	'svg'		=> array('image', 'image/svg+xml'),

	// text
	'conf'		=> array('text', 'text/plain'),
	'css'		=> array('text', 'text/css'),
	'htm'		=> array('text', 'text/html'),
	'html'		=> array('text', 'text/html'),
	'rtf'		=> array('text', 'text/richtext'),
	'txt'		=> array('text', 'text/plain'),
	'xml'		=> array('text', 'text/xml'),

	// video
	'mp4'		=> array('video', 'video/mp4'),
	'ogv'		=> array('video', 'video/ogg'),
	'webm'		=> array('video', 'video/webm'),
);


"Content-Type" Header Validation

1.4. HTML5


form

1.5. PHP


$_FILES

  • $_FILES['uploadedfile']['name']: The original name of the file on the client machine
  • $_FILES['uploadedfile']['type']: The mime type of the file
  • $_FILES['uploadedfile']['size']: The size of the file in bytes
  • $_FILES['uploadedfile']['tmp_name']: The temporary filename in which the uploaded file was stored on the server.

functions

  1. mime_content_type
  2. pathinfo
  3. hash_file

1.6. Image processing


  1. GD Graphics Library
  2. ImageMagick

check the availability

if (extension_loaded('gd'))
{
    print_r(gd_info());
}
else
{
    echo 'GD is not available.';
}

if (extension_loaded('imagick'))
{
    $imagick = new Imagick();
    print_r($imagick->queryFormats());
}
else
{
    echo 'ImageMagick is not available.';
}

2. WackoWiki file model


  1. global
    1. [file_name . extension]
    2. UPLOAD_GLOBAL_DIR
    3. direct file access
  2. local
    1. [@ . page_id . @ . file_name . extension]
    2. UPLOAD_PER_PAGE_DIR
    3. file access via file hadler
      1. checks page ACLs against user permissions

3. Database

  1. file_name
  2. author (user_id)
  3. file_description
  4. caption
  5. views
  6. uploaded_dt
  7. modified_dt
  8. credit
  9. exif
  10. context use (file_link)
  11. page_id
  12. categories
  13. access, viewing privacy
  14. file_lang
  15. file_size
  16. picture_w
  17. picture_h
  18. file_ext
  19. mime_type

4. Workflow


validation

  1. extension
  2. mime type
  3. size

header for file handler

"Content-Disposition: Attachment"
"X-Content-Type-Options: nosniff"


database

  1. mimetype (127+1+127 = 255)

https://stackoverflow.com/ques[..]n-storing-type-in-db


actions

  1. files
  2. upload

handler

  1. upload
  2. file
  3. attachments
  4. filemeta

functions

  • wacko -> link
  • wackoformatter

hacks


ideas

5. File Usage

Lotus Flower


The file syntax is processed in the link function. Not to mention that the link function requires a refactoring.
Additional we have missing cases for <audio> and <video> tag, which needs to be added.


In R5.5 a cation field was added to the file table.

<figure>
  <img src="https://wackowiki.org/doc/file/global/lotus_flower.jpg" alt="Lotus Flower" title="Lotus Flower (105 KiB)" width="640" height="427">
  <figcaption>Shinobazu Pond at Ueno Park at Ueno, Taito-ku in Tokyo. Original by <a href="https://www.flickr.com/photos/yoshikazut/">Yoshikazu TAKADA</a></figcaption>
</figure>


The file can be linked directly (file handler) or to filemeta handler.


Arguments can be added via ? and &


file:/image.png?200x400&direct&caption

5.1. Embedding

file:image.png
file:audio.opus
file:video.mp4

5.2. Alignment

file:image.png?right

5.3. Resizing

file:image.png?20x50

5.4. Linking

file:image.png?direct
file:image.png?nolink
file:image.png?linkonly


By default, clicking on an image should brings up the filemeta handler page.


How we manage to link an image to another page with the file: syntax?

  • ((file:/image.png some text))
  • ((/Page/Link file:/image.png))

5.5. Caching

file:image.png?nocache


5.6. Others

file:image.png?caption


+ audio and video options

6. links

  1. http://www.php.net/manual/en/features.file-upload.php
  2. https://www.owasp.org/index.ph[..]stricted_File_Upload
  3. https://www.acunetix.com/websi[..]upload-forms-threat/
  4. https://en.wikipedia.org/wiki/Media_type
  5. https://www.dokuwiki.org/images