Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x
?
Solved

prevent users from access a file folder form the web

Posted on 2014-03-06
6
Medium Priority
?
395 Views
Last Modified: 2014-03-12
my sql 5.5
ubuntu 12.04
php

Hi,

I created a PHP web application and I have a web page that allows users to upload word, pdf, and excel files.  Once the file is uploaded a confirmation page displays saying the file has been uploaded.  Problem: even though the url is encrypted a person can take the link and send it to a individual that does not have permission to the application  We created a session cookie for each logged in user but we are unable to protect the folder at the application level.  

Please share suggestions or examples about different ways to protect a folder from the public.   The only people that should have access are the users stored in user db table.  I am having trouble finding solutions.


Thanks.
0
Comment
Question by:cesemj
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 3
6 Comments
 
LVL 111

Accepted Solution

by:
Ray Paseur earned 2000 total points
ID: 39910714
This is a little complicated since it has a lot of moving parts, so please feel free to ask clarification questions.  I do not have all of the parts in the form of code examples, but I'm pretty sure that the design pattern I'm describing is sound.

Start by understanding PHP client authentication, because all of the pages you will use in this application are password protected.   The design pattern for PHP client authentication is given in this article.
http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/A_2391-PHP-login-logout-and-easy-access-control.html

You'll need a data base table that gives the identity of the authorized clients and the pseudo-URL locations of the files each client has uploaded.  I say "pseudo-url" because the information stored in the data base will tell the location of the uploaded files, but it will not really be a clickable URL; a special script on the server will be required to access the files.

You'll need a password-protected upload page.  The upload page will store the files in a special directory outside the WWW directory tree.  As each uploaded file is stored, this page will write a record of the file and its owner into the data base table.

You will need a password-protected display page for your authorized clients.  The page will be a "link farm" showing the names of all of the documents that the client has uploaded.

When the client hits the link farm page, she will have to log in, and the page will not work until the client is logged in with username and password.  In this way, your script can know who the user is.  The script can go to the data base table, and with the client identity, can gather the list of uploaded files to produce the link farm document.

The uploaded files will be stored in a directory outside of the WWW root (so they cannot be accidentally linked via a URL).  A password protected "force download" script can coordinate the information from the link farm and the client authentication data base.  It can read a single document out of the directory and force a download to the client machine.

Here is an example showing how to upload a file.  Your changes would be needed to add authentication, add the steps to record the upload in the data base, and move the uploaded files outside of the WWW root.

<?php // demo/upload_single_example.php
error_reporting(E_ALL);


// MANUAL REFERENCE PAGES YOU MUST UNDERSTAND TO UPLOAD FILES
// http://php.net/manual/en/features.file-upload.php
// http://php.net/manual/en/features.file-upload.common-pitfalls.php
// http://php.net/manual/en/function.move-uploaded-file.php
// http://php.net/manual/en/reserved.variables.files.php

// MANUAL PAGES THAT ARE IMPORTANT IF YOU ARE DEALING WITH LARGE FILES
// http://php.net/manual/en/ini.core.php#ini.upload-max-filesize
// http://php.net/manual/en/ini.core.php#ini.post-max-size
// http://php.net/manual/en/info.configuration.php#ini.max-input-time


// PHP 5.1+  SEE http://php.net/manual/en/function.date-default-timezone-set.php
date_default_timezone_set('America/Chicago');

// ESTABLISH THE BIGGEST FILE SIZE WE CAN ACCEPT - ABOUT 8 MB
$max_file_size = '8000000';

// ESTABLISH THE KINDS OF FILE EXTENSIONS WE CAN ACCEPT (USE UPPERCASE ONLY)
$file_exts = array
( 'XLS'
, 'XLSX'
)
;
$f_exts = implode(', ', $file_exts);

// ESTABLISH THE NAME OF THE DESTINATION FOLDER
$my_dir = getcwd();

// OR USE THIS TO PUT UPLOADS IN A SEPARATE FOLDER
$my_dir = 'RAY_junk';
if (!is_dir($my_dir))
{
    mkdir($my_dir);
}

// LIST OF THE ERRORS THAT MAY BE REPORTED IN $_FILES[]["error"] (THERE IS NO #5)
$errors = array
( 0 => "Success!"
, 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 => "UNDEFINED ERROR"
, 6 => "Missing a temporary folder"
, 7 => "Cannot write file to disk"
)
;


// IF THERE IS INFORMATION POSTED
if (!empty($_POST))
{
    // IF THERE ARE ERRORS
    $error_code    = $_FILES["userfile"]["error"];
    if ($error_code)
    {
        trigger_error($errors[$error_code], E_USER_ERROR);
    }

    // GET THE FILE SIZE
    $fsize = number_format($_FILES["userfile"]["size"]);

    // SYNTHESIZE THE NEW FILE NAME FOR TEMPORARY STORAGE
    $fname = basename($_FILES['userfile']['name']);

    // FAIL IF THIS IS NOT AN ALLOWABLE EXTENSION
    $f_ext = explode('.', $fname);
    $f_ext = end($f_ext);
    $f_ext = strtoupper(trim($f_ext));
    if (!in_array($f_ext, $file_exts)) trigger_error("$f_ext NOT ALLOWED.  CHOOSE FROM $f_exts", E_USER_ERROR);

    // THE SERVER PATH TO THE FILE
    $my_path
    = getcwd()
    . DIRECTORY_SEPARATOR
    . $my_dir
    . DIRECTORY_SEPARATOR
    . $fname
    ;

    // THE URL PATH TO THE FILE
    $my_url
    = $my_dir
    . DIRECTORY_SEPARATOR
    . $fname
    ;

    // MESSAGES ABOUT THE UPLOAD STATUS, IF ANY
    $msg = NULL;

    // IF THE FILE IS NEW (DOES NOT EXIST)
    if (!file_exists($my_path))
    {
        // IF THE MOVE FUNCTION WORKED CORRECTLY
        if (move_uploaded_file($_FILES['userfile']['tmp_name'], $my_path))
        {
            $upload_success = 1;
        }
        // IF THE MOVE FUNCTION FAILED IT PROBABLY THREW A MESSAGE
        else
        {
            $upload_success = -1;
            trigger_error("MOVE TO $my_path FAILED", E_USER_ERROR);
        }
    }

    // IF THE FILE ALREADY EXISTS
    else
    {
        $msg .= "<br/><b><i>$my_url</i></b> already exists" . PHP_EOL;

        // SHOULD WE OVERWRITE THE FILE? IF NOT
        if (empty($_POST["overwrite"]))
        {
            $upload_success = 0;
        }

        // IF WE SHOULD OVERWRITE THE FILE, TRY TO MAKE A BACKUP
        else
        {
            $now    = date('Y-m-d-His');
            $my_bak = $my_path . '.' . $now . '.bak';
            if (!copy($my_path, $my_bak))
            {
                $msg .= "<br/><strong>Attempted Backup Failed!</strong>" . PHP_EOL;
            }
            if (move_uploaded_file($_FILES['userfile']['tmp_name'], $my_path))
            {
                $upload_success = 2;
            }
            else
            {
                $upload_success = -1;
                trigger_error("MOVE TO $my_path FAILED", E_USER_ERROR);
            }
        }
    }

    // PREPARE A REPORT OF THE SCRIPT'S SUCCESS OR FAILURE
    if ($upload_success == 2) { $msg .= "<br/>A backup was made and the file was overwritten" . PHP_EOL; }
    if ($upload_success == 1) { $msg .= "<br/><strong>$my_url</strong> has been saved" . PHP_EOL; }
    if ($upload_success == 0) { $msg .= "<br/><strong>It was NOT overwritten.</strong>" . PHP_EOL; }
    if ($upload_success < 0)  { $msg .= "<br/><strong>ERROR: $my_url NOT SAVED - SEE WARNING FROM move_uploaded_file()</strong>" . PHP_EOL; }

    // ADD FILE SIZE AND PERMISSION INFORMATION
    if ($upload_success > 0)
    {
        $msg .= "<br/>$fsize bytes uploaded" . PHP_EOL;
        if (!chmod ($my_path, 0755))
        {
            $msg .= "<br/>chmod(0755) FAILED: fileperms() = ";
            $msg .= substr(sprintf('%o', fileperms($my_path)), -4);
        }
    }

    // SHOW THE SUCCESS OR FAILURE
    echo $msg;

    // SHOW A LINK TO THE FILE
    echo '<br/>'
    . '<a href="'
    . $my_url
    . '" target="_blank">'
    . "See: $my_url"
    . '</a>'
    ;
}


// CREATE THE FORM FOR INPUT (USING HEREDOC SYNTAX)
$form = <<<ENDFORM
<h2>Upload a File</h2>
<p>
<form enctype="multipart/form-data" method="post">
<!-- MAX_FILE_SIZE MUST PRECEDE THE FILE INPUT FIELD -->
<input type="hidden" name="MAX_FILE_SIZE" value="$max_file_size" />
Find a file to Upload ($f_exts): <input name="userfile" type="file" />
<br/>Check this box
<input autocomplete="off" type="checkbox" name="overwrite" /> to <strong>overwrite</strong> existing files
</br><input type="submit" value="Upload" />
</form>
</p>
ENDFORM;

echo $form;

Open in new window

Here is a script that can force a download.  Again, you would want to add client authentication and the data base lookup so you can find the file in the non-WWW directory.

<?php // demo/force_download.php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('log_errors',     TRUE);


// DEMONSTRATE HOW TO CAUSE A FILE DOWNLOAD


// REQUIRED FOR USE WITH THE PHP date() FUNCTIONS
date_default_timezone_set('America/New_York');

// A FILE TO DOWNLOAD - THIS LINK COULD COME IN THE URL VIA $_GET, OR COULD BE GENERATED INSIDE THE SCRIPT
$url = "http://www.IcoNoun.com/demo/short_text_file.txt";

// THE USE CASE FOR THE FUNCTION
force_download($url);


// FUNCTION TO FORCE A DOWNLOAD FROM A FILE
function force_download($filename)
{
    // GET THE CONTENTS OF THE FILE
    $filedata = @file_get_contents($filename);

    // SUCCESS
    if ($filedata)
    {
        // GET A NAME FOR THE FILE
        $basename = basename($filename);

        // THESE HEADERS ARE USED ON ALL BROWSERS
        header("Content-Type: application-x/force-download");
        header("Content-Disposition: attachment; filename=$basename");
        header("Content-length: ".(string)(strlen($filedata)));
        header("Expires: ".gmdate("D, d M Y H:i:s", mktime(date("H")+2, date("i"), date("s"), date("m"), date("d"), date("Y")))." GMT");
        header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");

        // THIS HEADER MUST BE OMITTED FOR IE 6+
        if (FALSE === strpos($_SERVER["HTTP_USER_AGENT"], 'MSIE '))
        {
            header("Cache-Control: no-cache, must-revalidate");
        }

        // THIS IS THE LAST HEADER
        header("Pragma: no-cache");

        // FLUSH THE HEADERS TO THE BROWSER
        flush();

        // WRITE THE FILE
        echo $filedata;
    }

    // ERROR
    else
    {
        trigger_error("ERROR: UNABLE TO OPEN $filename", E_USER_ERROR);
    }
}

Open in new window

HTH, ~Ray
0
 

Author Comment

by:cesemj
ID: 39911065
Thank you Ray, I am going review, test, and let you know if I am sinking or rising.
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 39912160
Ha!  I expect you'll be paddling for a while before anything happens :-)
0
Create CentOS 7 Newton Packstack Running Keystone

A bug was filed against RDO for the installation of Keystone v3. This guide is designed to walk you through the configuration for using Keystone v3 with Packstack. You will accomplish this using various repos and the Answers file.

 

Author Comment

by:cesemj
ID: 39916573
I am still paddling ad thank you for checking in on me!
0
 

Author Comment

by:cesemj
ID: 39922428
Thank you for sharing your insight and subject matter expertise.  The direction you presented allowed us to to implement a important safeguard.  You Rock!!!!!!!!!!!!
0
 
LVL 111

Expert Comment

by:Ray Paseur
ID: 39923700
Thanks for the points and for your kind words, and thanks for using EE! ~Ray
0

Featured Post

Free Backup Tool for VMware and Hyper-V

Restore full virtual machine or individual guest files from 19 common file systems directly from the backup file. Schedule VM backups with PowerShell scripts. Set desired time, lean back and let the script to notify you via email upon completion.  

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article shows the steps required to install WordPress on Azure. Web Apps, Mobile Apps, API Apps, or Functions, in Azure all these run in an App Service plan. WordPress is no exception and requires an App Service Plan and Database to install
This article discusses how to implement server side field validation and display customized error messages to the client.
Learn several ways to interact with files and get file information from the bash shell. ls lists the contents of a directory: Using the -a flag displays hidden files: Using the -l flag formats the output in a long list: The file command gives us mor…
How to Install VMware Tools in Red Hat Enterprise Linux 6.4 (RHEL 6.4) Step-by-Step Tutorial

721 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question