0

I use nginx as server and php7. I followed some instructions and must have made something wrong.

The form:

<div class="collapse" id="upload_avatar">
    <div class="card card-body">
        <form enctype="multipart/form-data" action="" method="post">
            <p class="text-left">Upload Avatar:</p>
            <input type="hidden" name="MAX_FILE_SIZE" value="300000" />
            <input name="image" type="file" /><br>
            <button class="form-control mr-sm-2 btn btn-outline-success my-2 my-sm-0" type="submit" name="avatar_upload" aria-controls="collapse_upload_avatar">
                Upload
            </button>
        </form>
    </div>
</div>

The php part:

if(isset($_POST["avatar_upload"])){
    $verifyimg = getimagesize($_FILES['image']['tmp_name']);

    if($verifyimg['mime'] != 'image/png') {
        echo "Only PNG images are allowed!";
        exit;
    }    

    $uploaddir = '/members/3/';
    $uploadfile = $uploaddir . basename($_FILES['image']['name']);

    if (move_uploaded_file($_FILES['image']['tmp_name'], $uploadfile)) {
        echo "File is valid, and was successfully uploaded.<br>";
    } else {
        echo "Possible file upload attack!<br>";
    }

    echo '<pre>';
    echo 'info:';
    print_r($_FILES);
    print "</pre>";
}

It prints out:

Possible file upload attack!
info:Array
(
    [image] => Array
        (
            [name] => Selection_001.png
            [type] => image/png
            [tmp_name] => /tmp/phpGpp3rB
            [error] => 0
            [size] => 299338
        )
)

There is no /tmp/php* file

There is no file in the /members/3/ directory

The permission is 777 for /members and /members/3

nginx/error.log shows: PHP message: PHP Warning: move_uploaded_file(/members/3/Selection_001.png): failed to open stream: No such file or directory ... on line 197 PHP message: PHP Warning: move_uploaded_file(): Unable to move '/tmp/phpGpp3rB' to '/members/3/Selection_001.png'

Is a nginx setting missing???

Shawn C.
  • 6,446
  • 3
  • 34
  • 38
Ronald Wiplinger
  • 2,033
  • 3
  • 14
  • 20

2 Answers2

2

Remove / in your members directory.

$uploaddir = 'members/3/';

0

In reference to your last question, no, this most definitely sounds like a PHP or coding issue. Nginx is barely a proxy to get PHP to handle the request. However, without more information, it is difficult to identify your issue with certainty. There are a few items that I don't see addressed in your code, that may lead to the culprit.

  1. Do you really have an absolute path for /members/3/? That directory does -- in fact -- reside at the top of your filesystem? Most setups have file uploads somewhere within the webroot rather than at prominent places in the filesystem hierarchy. For example Wordpress puts its uploads in wp-content/uploads/, which itself exists inside of a webroot (e.g. /var/www/).

    What does PHP say?

    if ( file_exists ( '/members/3/' ) )
        echo "/members/3/ truly does exist, anchored to the root";
    else
        echo "NOPE!  Can't find this directory!";
    
  2. Does the file $_FILES['image']['name'] actually exist? It is not enough to use getimagesize(), as explicitly cautioned in the documentation. In particular:

    Caution This function expects filename to be a valid image file. If a non-image file is supplied, it may be incorrectly detected as an image and the function will return successfully, but the array may contain nonsensical values.

    Do not use getimagesize() to check that a given file is a valid image. Use a purpose-built solution such as the Fileinfo extension instead.

Other items of note:

  1. It is beyond time for the misunderstanding that is MAX_FILE_SIZE to die.

    <input type="hidden" name="MAX_FILE_SIZE" value="300000" />
    

    That is referenced in precisely one authoritative spot on the internet: the PHP documentation, and it was maybe utilized once upon a time by older browsers to check file size. Today, it is not utilized by any browser of which I'm aware. From the documentation:

    The MAX_FILE_SIZE hidden field (measured in bytes) must precede the file input field, and its value is the maximum filesize accepted by PHP. This form element should always be used as it saves users the trouble of waiting for a big file being transferred only to find that it was too large and the transfer failed. Keep in mind: fooling this setting on the browser side is quite easy, so never rely on files with a greater size being blocked by this feature. It is merely a convenience feature for users on the client side of the application. The PHP settings (on the server side) for maximum-size, however, cannot be fooled.

    In 2018, this hidden item has precisely zero value -- and is potentially detrimental if your scripts at all rely on it. You should remove it.

  2. Your code snippet perhaps culled it for brevity, but I don't see a wrapping

    if ( 'POST' === $_SERVER['REQUEST_METHOD'] ) {
    

    That is the proper method to check if the request was a POST. In 90% of cases, it is enough to know that a $_POST[...] variable was set, but sometimes the $_POST variable is completely empty (e.g., if the post exceeds post_max_size).

  3. In the absence of knowledge about your setup and security, it is dangerous to blindly trust user input:

    $uploaddir = '/members/3/';
    $uploadfile = $uploaddir . basename($_FILES['image']['name']);
    

    A simple crack might see a hapless user overwrite an already existing file. A more cunning attack might use this vector with a specially crafted image file to exploit your server.

  4. Have you enabled all error messages? Put these lines at an appropriate spot in your script:

    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);
    
  5. What are your relevant PHP variables? Your file looks to be < 300K and so is probably fine, but all the same, what is the value of post_max_size, upload_max_filesize, and file_uploads.

  6. Have you perused the output of phpinfo()? Does anything stand out?

  7. Have you tried to putting a sleep(30) command in before the script exits, and manually verifying that the file is indeed in place (e.g., in /tmp/php...)? If it does exist, what does a command line file say of it:

    $ file /tmp/php...
    
  8. If none of the above is fruitful, go through "PHP file uploading checklist".

hunteke
  • 3,648
  • 1
  • 7
  • 17