Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<ol> <li><p><strong>Allow only authorized users to upload a file.</strong> You can add a captcha as well to hinder primitive bots.</p></li> <li><p>First of all, <strong>set the <code>MAX_FILE_SIZE</code> in your upload form</strong>, and set the <strong>maximum file <code>size</code> and <code>count</code> on the server</strong> as well. </p> <pre><code>ini_set('post_max_size', '40M'); //or bigger by multiple files ini_set('upload_max_filesize', '40M'); ini_set('max_file_uploads', 10); </code></pre> <p>Do size check by the uploaded files:</p> <pre><code>if ($fileInput['size'] &gt; $sizeLimit) ; //handle size error here </code></pre></li> <li><p>You should <strong>use <code>$_FILES</code> and <a href="https://stackoverflow.com/questions/16276835/php-move-uploaded-file-why-is-it-important"><code>move_uploaded_file()</code></a></strong> to put your uploaded files into the right directory, or if you want to process it, then check with <code>is_uploaded_file()</code>. (These functions exist to prevent <em>file name injections</em> caused by <code>register_globals</code>.)</p> <pre><code>$uploadStoragePath = '/file_storage'; $fileInput = $_FILES['image']; if ($fileInput['error'] != UPLOAD_ERR_OK) ; //handle upload error here, see http://php.net/manual/en/features.file-upload.errors.php //size check here $temporaryName = $fileInput['tmp_name']; $extension = pathinfo($fileInput['name'], PATHINFO_EXTENSION); //mime check, chmod, etc. here $name = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); //true random id move_uploaded_file($temporaryName, $uploadStoragePath.'/'.$name.'.'.$extension); </code></pre> <p>Always <strong>generate a random id instead of using the original file name</strong>.</p></li> <li><p>Create a <strong>new <em>subdomain</strong> for example <a href="http://static.example.com" rel="nofollow noreferrer">http://static.example.com</a></em> or at least a new directory outside of the <code>public_html</code>, for the uploaded files. This <em>subdomain</em> or directory <strong>should not execute any file</strong>. Set it in the server config, or set <a href="https://stackoverflow.com/questions/18932756/disable-all-cgi-php-perl-for-a-directory-using-htaccess">in a <strong><code>.htaccess</code> file</strong></a> by the directory.</p> <pre><code> SetHandler none SetHandler default-handler Options -ExecCGI php_flag engine off </code></pre> <p>Set it <a href="https://stackoverflow.com/questions/828172/how-do-i-secure-a-web-servers-image-upload-directory"><strong>with <code>chmod()</code></strong></a> as well.</p> <pre><code> $noExecMode = 0644; chmod($uploadedFile, $noExecMode); </code></pre> <p>Use <code>chmod()</code> on the newly uploaded files too and set it on the directory.</p></li> <li><p>You should <strong>check the <em>mime type</em></strong> sent by the hacker. You should create a <em>whitelist</em> of allowed <em>mime types</em>. <strong>Allow images only</strong> if any other format is not necessary. Any other format is a security threat. Images too, but at least we have tools to handle them...<br> The <em>corrupted content</em> for example: <em>HTML</em> in an image file can cause <em>XSS</em> by browsers with <a href="https://stackoverflow.com/questions/18337630/what-is-x-content-type-options-nosniff"><em>content sniffing</em> vulnerability</a>. When the corrupted content is a <em>PHP</em> code, then it can be combined with an <em>eval injection</em> vulnerability.</p> <pre><code>$userContent = '../uploads/malicious.jpg'; include('includes/'.$userContent); </code></pre> <p>Try to avoid this, for example use a <code>class autoloader</code> instead of including php files manually...<br> By handling the <em>javascript injection</em> at first you have to <strong>turn off <em>xss</em> and <em>content sniffing</em> in the browsers</strong>. <em>Content sniffing</em> problems are typical by older <em>msie</em>, I think the other browsers filter them pretty well. Anyways you can prevent these problems with a bunch of headers. (Not fully supported by every browser, but that's the best you can do on client side.)</p> <pre><code>Strict-Transport-Security: max-age={your-max-age} X-Content-Type-Options: nosniff X-Frame-Options: deny X-XSS-Protection: 1; mode=block Content-Security-Policy: {your-security-policy} </code></pre> <p>You can check if a file is corrupted with <a href="http://www.php.net/manual/en/imagick.identifyimage.php" rel="nofollow noreferrer"><code>Imagick identify</code></a>, but that does not mean a complete protection.</p> <pre><code>try { $uploadedImage = new Imagick($uploadedFile); $attributes = $uploadedImage-&gt;identifyImage(); $format = $image-&gt;getImageFormat(); var_dump($attributes, $format); } catch (ImagickException $exception) { //handle damaged or corrupted images } </code></pre> <p>If you want to serve <strong>other <em>mime types</em>, you should always <em>force download</em></strong> by them, never <em>include</em> them into webpages, unless you really know what you are doing...</p> <pre><code>X-Download-Options: noopen Content-Disposition: attachment; filename=untrustedfile.html </code></pre></li> <li><p>It is possible to have valid image files with code inside them, for example in <em>exif</em> data. So you have to <strong>purge <em>exif</em> from images</strong>, if its content is not important to you. You can do that with <em><code>Imagick</code></em> or <em><code>GD</code></em>, but both of them requires repacking of the file. You can find an <em><code>exiftool</code></em> as an alternative. I think the simplest way to clear <em>exif</em>, is loading images with <em>GD</em>, and <a href="https://stackoverflow.com/questions/18531457/php-clear-png-metadata-but-keep-similar-quality">save them as <em>PNG</em> with highest quality</a>. So the images won't lose quality, and the <em>exif</em> tag will be purged, because <em>GD</em> cannot handle it. Make this with images uploaded as <em>PNG</em> too...<br> If you want to extract the <em>exif</em> data, never use <code>preg_replace()</code> if the <code>pattern</code> or <code>replacement</code> is from the user, because that will lead to an <em>eval injection</em>... Use <code>preg_replace_callback()</code> instead of the <code>eval regex flag</code>, if necessary. (Common mistake in copy paste codes.) <em>Exif</em> data can be a problem if your site has an <em>eval injection</em> vulnerability, for example if you use <code>include($userInput)</code> somewhere. </p></li> <li><p><strong>Never ever use <code>include()</code>, <code>require()</code> by uploaded files</strong>, serve them as static or use <code>file_get_contents()</code> or <code>readfile()</code>, or any other file reading function, if you want to control access.<br> It is rarely available, but I think the best approach to use the <code>X-Sendfile: {filename}</code> headers with the <a href="https://tn123.org/mod_xsendfile/" rel="nofollow noreferrer">sendfile apache module</a>. By the headers, never use user input without validation or sanitization, because that will lead to <em>HTTP header injection</em>.<br> If you don't need <em>access control</em> (means: only authorized users can see the uploaded files), then serve the files with your webserver. It is much faster... </p></li> <li><p><strong>Use an antivir</strong> to check the uploaded files, if you have one.</p></li> <li><p>Always <strong>use a combined protection</strong>, not just a single approach. It will be harder to breach your defenses...</p></li> </ol>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload