Jump to content

How to delete a file from a button?


A JM

Recommended Posts

I'm trying to delete a file but am having some problems, can someone help? The script doesn't seem to be working as I don't get prompted "Are you sure?"...

 

Any help would be appreciated - maybe there is an easier way to do this?

 

Thanks.

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php
   // user has clicked a delete hyperlink
   if($_GET['action'] && $_GET['action'] == 'delete') {
       unlink($_GET['filedelete']);
       header("Location:files.php");
       exit();
   }
?>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
</head>

<body>
<?php $path = "../temp/" . $_GET['ID'] . "/";
        $dir = dir($path);
        while($file = $dir->read())
        {
         if($file != '.' && $file != '..')

echo '<a href="' . $path . $file . '">' . $file . '</a>     <input type="button" value="Delete" name=filedelete onClick="if(confirm(Are you sure?)){ window.location=\adm_file_list.php?delete=' .$path . $file. '\'; }"><br/>';

}?>

</body>
</html>

Link to comment
Share on other sites

You can't modify your headers if their is already output:

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php
   // user has clicked a delete hyperlink
   if($_GET['action'] && $_GET['action'] == 'delete') {
       unlink($_GET['filedelete']);
       header("Location:files.php");
       exit();
   }
?>

 

modify to or use http://be.php.net/manual/en/ref.outcontrol.php:

 

<?php
   // user has clicked a delete hyperlink
   if($_GET['action'] && $_GET['action'] == 'delete') {
       unlink($_GET['filedelete']);
       header('Location:files.php'); // if you don't need string parsing use ' instead of "
       exit();
   }
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

 

I must advice you to validate your input imagine what this url will do:

files.php?action=delete&filedelete=index.php

files.php?action=delete&filedelete=files.php

 

Link to comment
Share on other sites

I see your point about validating the URL...

 

So, how should I go about doing this? I need the user to be able to delete a file by either a hyperlink, button, etc.

 

Since the page lists the files in a directory after the delete I need to refresh the page... this is definitely new territory for me.

 

 

Link to comment
Share on other sites

There are a LOT of problems with that script.

 

1. The problem with the confirmation has nothing to do with SCRIPT tags. You don't need script tags when defining the action for a trigger. The reason you are not getting a confirmation is that the confirmation text is not withing quotes.

 

2. The processing part of the script is looking for the variables $_GET['action'] & $_GET['filedelete'] - neither of which exist. 'action' doesn't appear anywhere and 'filedelete' is the name of the button. But since you are not POSTing the data it won't exist in the receiving page. Only the values passed in the query sting (e.g. 'delete') will be accessible via GET on the receiving page.

 

3. You have HTML content before the header() function (as noted by ignace)

 

All in all, this is a terrible script. I don't intend to be mean, but you are allowing someone to delete files without any security whatsoever. Even the manner you are allowing the user to select a folder is problematic as the user could specify any directory not just one within the 'temp' folder. They could, for example, use '../' as part of the ID value to potentially point to just about ANYWHERE on the web server not just the web root.

 

When writing any code that accepts user input always assume that the user is providing information with malicious intent.

Link to comment
Share on other sites

mjdamato,

 

Thank you for your constructive criticism.

 

I can see now that there are issues with how I was trying to implement this script.

 

My problem still persists that I need to find a way to allow a user logged into the system to be able to delete a file that exists in a particular directory, securely.

 

Do you have some suggestions on how I should go about achieving this?

 

Thanks for you input.

 

A JM,

 

Link to comment
Share on other sites

I see your point about validating the URL...

So, how should I go about doing this? I need the user to be able to delete a file by either a hyperlink, button, etc.

Since the page lists the files in a directory after the delete I need to refresh the page... this is definitely new territory for me.

 

Make sure the filedelete is within the user directory

define('USERS_DIRECTORY_PATH', realpath('/path/to/users/directory'));

// http://shiflett.org/articles/session-hijacking
$user = $_SESSION['username'];
if (!is_valid_username($user)) { // be sure to use this on your authentication or before storing
    trigger_error('..error messages for admins..'); // use in conjunction with set_error_handler() which stores the information in a database or sends an e-mail..
    die('..personalised error message for the user..');
}

// optional: check the user against the database and get a hard-to-guess type of directory (/path/to/users/directories/q3547stghbq6347htq77jy46q7j6q7/*.*)
$directory = implode(DIRECTORY_SEPARATOR, array(USERS_DIRECTORY_PATH, $user));
if ($action == 'delete') {
    $user_file = filter_get_user_file($_GET['filedelete']);
    if (is_user_file($userfile, $directory)) {
        delete_user_file($userfile, $directory);
    }
}

function filter_get_user_file($input_userfile) {
    return basename($input_userfile);
}

function delete_user_file($userfile, $directory) {
    return unlink(implode(DIRECTORY_SEPARATOR, array($directory, $userfile)));
}

function is_valid_username($username) {
    return ctype_alpha($username);
}

function is_user_file($file, $userdirectory) {
    return file_exists(implode(DIRECTORY_SEPARATOR, array($userdirectory, $file)));
}

 

These functions are not closing but they give you a good start

Link to comment
Share on other sites

If I may make a further suggestion on how to do this properly.

You need to establish an area in which you are going to allow them to delete. Never display any hierarchy information that is beyond the folder in which they are allowed to delete. So you will need to check that the folder always contains the appropriate directory tree. Then before you allow them to see there options for deleting you need to then strip this portion of the directory tree from your strings. Then the user has a sense of being at a root, but only the root you select. Make sure you always re validate that the file exist with in the directory that you have set. To establish this directory and to maintain it, either hard code it or put it in there session, but never allow the user to modify that piece of session data. Hope that helps you understand a more secure concept.

Link to comment
Share on other sites

I meant to also note that this page is an Iframe and in fact does limit the user by using the $user = $_SESSION['username']; I believe that it will work correctly this way, the user logs into the site and needs permission to see the main page and my Iframe is embedded in the main page.

 

Of this entire project that I've been working on and subjecting the list to numerous questions of the "file" routines have been the worst and most time consuming, there has to be easier ways to get things like this done, arrgh..

 

WolfRage , your point brings me to .htaccess, cant' this be achived by an .htacess file? From what I've been reading .htacees can restrict the user and perform the operations that I want but the downfall I see is that the user has to have credentials on the box - correct me if I'm wrong. Having credentials on the box are not the same as a $user = $_SESSION['username'];...

 

A JM,

 

Link to comment
Share on other sites

The problem with the .htaccess method is that you are using PHP to excute the commands. Thus the system is executing the commands not the user. So you need to be more proactive by creating a virtual play ground for your scripts to ensure no deleting happens in any other place.

Link to comment
Share on other sites

I hate to reinvent the wheel...

 

How do others handle similar situations? I mean there are all kinds of sites that allow users to upload and delete files from directory's how are they doing?

 

Any links or documents of how to implement?

 

Thanks.

Link to comment
Share on other sites

Here, now try to figure the rest out on your own.

 <?php
$file=explode('=',clean($_POST['file']));
if($file[0]==='file'){
       $file=$_SESSION['project_scope'].'/'.$file[1];
       $var=realpath($file);
        if(strpos($var,$_SESSION['project_scope'])!==FALSE) {
            unlink($var);
            $var=basename($var);
            echo $var.' was successfully deleted.';
        }
}
?>

Better than giving a man a fish is teaching him to fish.

Link to comment
Share on other sites

How do others handle similar situations? I mean there are all kinds of sites that allow users to upload and delete files from directory's how are they doing?

 

I can't tell you how others do it but i can tell you how i would do it: Using access-control the same principle OS's use for sharing files (both user-based as role or group-based).

 

tables (very simple user-based access control):
------------
users (id)
files (id, owner, fullpath);

 

-- If the user uploads a (non-malicious) file:
INSERT INTO files (id, owner, fullpath) VALUES (NULL, $user_id, $fullpath);
-- View user files:
SELECT * FROM files WHERE owner = $user_id
-- Remove a user file
DELETE FROM files WHERE fullpath = $fullpath AND owner = $user_id

Link to comment
Share on other sites

ignace,

 

I do use a table for user logins to the site and the user is validated by username and password.

 

ignace - with regard to using a table to store paths and file names, etc. just deleting the record from the table doesn't delete the file from the server - so the server would end up with a bunch of abandoned files, correct?

 

WolfRage - you knew it was coming...  I understand the script and that it requires a $_POST variable 'file' to be passed. Could I do this with a hiddentext object? would that make sense? when the user clicks the button the file and path are passed to the page and then retrievable by $_GET()?

 

mjdamato  - I also see now the errors in what I posted.

 

...Even the manner you are allowing the user to select a folder is problematic as the user could specify any directory not just one within the 'temp' folder. They could, for example, use '../' as part of the ID value to potentially point to just about ANYWHERE on the web server not just the web root.

 

Since I'm new to this - how would one go about performing the above?

 

 

 

 

 

Link to comment
Share on other sites

ignace - with regard to using a table to store paths and file names, etc. just deleting the record from the table doesn't delete the file from the server - so the server would end up with a bunch of abandoned files, correct?

 

That is indeed correct but it was just a sample and part of something bigger (as we all are ;)) And would end up in something like:

 

function delete_user_file($userfile) {
    global $db; // retrieve db connection
    $userid = $_SESSION['userid'];
    // assuming the programmer performed an is_user_file($userfile)
    $query = 'DELETE FROM files WHERE owner = %u AND fullpath = "%s"';
    $result = mysql_query(sprintf($query, $userid, $userfile), $db);
    return mysql_affected_rows($result) > 0 ? true : false;
}

Link to comment
Share on other sites

Gotcha ignace.. thanks. I was just making sure I wasn't missing something...

 

I'll start down the path of using a hiddentext object and see if I can make some progress.

 

So, I'm clearly thinking about this - since the user needs credentials to login to the main page before they can even see the list of files to delete on the Iframe, is this acceptable?

 

Also, I understand that I should eliminate paths that the user will see and they should only see the current path for the file deletion.

 

WolfRage - I get an error on the clean() function is this a PHP function or a custom routine?

 

$file=explode('=',clean($_POST['file']));

 

 

Link to comment
Share on other sites

WolfRage - I get an error on the clean() function is this a PHP function or a custom routine?

$file=explode('=',clean($_POST['file']));

 

Yes this is a custom function he probably meant it as some sort of prototype for which you still have to write out the body.. heart, lungs, .. just kidding ;)

Link to comment
Share on other sites

Been banging on it for a while now and could use some help?? I made some changes and have not yet gotten to the validation pieces or hiding the pathing, just trying to get a simple list of files at the moment.

 

I'm a little confused - in the while statement there is the if statement to eliminate "." and ".." from the file list but they are showing up?

 

 

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<?php
if(isset($_GET['delete']) && $_GET['delete']=='true') {
print_r(unlink("../claims/" . $_GET['ID'] . "/".$_POST['filedelete']));
exit;
   //unlink("../temp/" . $_GET['ID'] . "/".$_POST['filedelete']);
   header('location: adm_file_list.php'); die();
}
?>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title></title>
</head>

<body>
<?php $path = "../temp/" . $_GET['ID'] . "/";
$dir = dir($path);
while($file = $dir->read())
{
 if($file != '.' && $file != '..')

echo "<form method=\"post\" name=\"filedelete\" action="?><?php echo $_SERVER['PHP_SELF'].'?delete=true';?><?php echo" >
         <a href= $path$file >$file</a>
         <input type=\"submit\" value=\"Delete\"><br/>";
else
}
?>

</body>
</html>

 

Any help would be appreciated...

 

A JM,

Link to comment
Share on other sites

Thanks Wolfrage,

 

I finally got a working prototype... now I'd like to lock it down and have it be more secure.

 

Looking back at your script what is it that your doing, verifying that the file exists before attempting to delete it?

 

 <?php
$file=explode('=',clean($_POST['file']));
if($file[0]==='file'){
       $file=$_SESSION['project_scope'].'/'.$file[1];
       $var=realpath($file);
        if(strpos($var,$_SESSION['project_scope'])!==FALSE) {
            unlink($var);
            $var=basename($var);
            echo $var.' was successfully deleted.';
        }
}
?>

 

Never display any hierarchy information that is beyond the folder in which they are allowed to delete.

 

I believe I understand what you mean - don't display the full path to the file being linked or deleted. How would you suggest I go about this given the script below?

 

The problem with the .htaccess method is that you are using PHP to excute the commands. Thus the system is executing the commands not the user. So you need to be more proactive by creating a virtual play ground for your scripts to ensure no deleting happens in any other place.

 

I understand your point, I think this may als be related to my question above with regard pathing...

 

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<?php
$path = "../temp/" . $_GET['ID'] . "/";

if(isset($_GET['delete']) && $_GET['delete']=='true') {
        print_r($_POST['pathfile']);
        exit;
   //unlink($_POST['pathfile']);
   header('location: adm_file_list.php'); die();
}
?>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>

<body>
<?php
$path = "../temp/" . $_GET['ID'] . "/";
$dir = dir($path);
while($file = $dir->read())
{
 if($file != '.' && $file != '..'){
echo "<form method='post' name='filedelete' action="?><?php echo $_SERVER['PHP_SELF'].'?delete=true';?>
<?php echo" ><a href= $path$file >$file</a>    <input type='hidden' name='pathfile' value= $path$file><input type='submit' value='Delete'></form>";
}
}
?>

</body>
</html>

Link to comment
Share on other sites

Alright going to break out my logic for you, because like Ignace said this was a peice of something much larger as yours will become.

<?php//comments will be for the line above.
$file=explode('=',clean($_POST['file']));
/* ^ My Script would take in  a Post var named file. I then ran it through my custom function which proceeds to clean the variable of malicious logic. I then explode this Post Variable around the = sign to give me the first part and the second part in an array. */
if($file[0]==='file'){
/* ^ Here I am checking to make sure it is a file. I actually have a second if statement that checks if it is set to "dir" for directory. My listing function on the pervious page actually breaks them out and assigns them as a file or directory. */
       $file=$_SESSION['project_scope'].'/'.$file[1];
/* ^ This is what you really want to know! Each section has a project area the area I designated in which files may be stored by a user. This var is stored in $_SESSION['project_area']. So I am joing the strings together to form the complete file path on the server. But by storing the rest of the file path in the session var the user never really knows where the files are on the server. */
       $var=realpath($file);
       /* ^ Here I am just making sure to convert it to a realpath in case the system was windows based. */
        if(strpos($var,$_SESSION['project_scope'])!==FALSE) {
        /* ^ Yes for security I am double checking to make sure that the file is actually under the correct project_scope. This is redundant and could be removed. It does not verify if the file exist, although you could. */
            unlink($var);
            /* ^ Delete the file. */
            $var=basename($var);
            /* ^ Get just the name of the file. */
            echo $var.' was successfully deleted.';
            /* ^ Confirm deletion. */
        }
}
?>

Now your next question is more complex. At this point the designated location will have to be wired into your display script, so that you can strip out the top level information.

I also included a protected status of which was also built into the scripts. This allowed me to store hidden files that would help to maintian the system.

<?php
  function ls($dir) {
        ?>
        <table border="1" width="100%" class='left'>
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Size</th>
                </tr>
            </thead>
            <tbody>
        <?php
        $handle = dir($dir);
        while ($filename = $handle->read()) {
            if($filename!=='.') {
                $file=realpath("$dir/$filename");
                $file=str_replace($_SESSION['project_scope'],'',$file);
                if (is_dir("$dir/$filename")) {
                    $protected=FALSE;
                    foreach($_SESSION['PC-protected'] as $value){
                        if($filename===$value){
                            $protected=TRUE;
                        }
                    }
                    if($protected===FALSE){
                        if (is_readable("$dir/$filename")) {
                            if($_SESSION['project_scope']===realpath($file.'/'.$_SESSION['project'])) {
                                unset($line); //Do not show the option to move higher in the directory structure.
                            }
                            else{
                                $line = '<tr><td><a href="'.$_SERVER['SELF'].'?dir='.$file.'">'.$filename.'/</a></td><td>Directory</td></tr>';
                            }
                        }
                        else {
                            $line = '<tr><td>'.$filename.'/</td><td>'.$size.'</td></tr>';
                        }
                    }
                    else {
                        unset($line);
                    }
                }       
                else {
                    $size = filesize("$dir/$filename");
                    $protected=FALSE;
                    foreach($_SESSION['PC-protected'] as $value){
                        if($filename===$value){
                            $protected=TRUE;
                        }
                    }
                    if($protected===FALSE){
                        if (is_readable("$dir/$filename")) {
                            $line = '<tr><td><a href="'.$_SERVER['SELF'].'?file='.$file.'">'.$filename.'</a></td><td>'.$size.'</td></tr>';
                        }
                        else {
                            $line = '<tr><td>'.$filename.'/</td><td>'.$size.'</td></tr>';
                        }
                    }
                    else {
                        unset($line);
                    }
                }
                if(isset($line)){
                    echo $line."\n";
                }
            }
        }
        $handle->close();
        ?>
            </tbody>
        </table>
        <?php
    }

?>
Link to comment
Share on other sites

WolfRage,

 

Thanks for the explanation things are a little clearer.

 

I'm curious about moving the storage folder outside of the webroot to eliminate access directly to the folder. I think this will work but am curios about creating links to the documents what do you use for pathing so the user can download or open the document?

 

I also so an interesting concept by MadTechie for limiting access to files from the outside (http://www.phpfreaks.com/forums/index.php?topic=257191.0) what do you think about that?

 

Thanks.

 

A JM,

 

Link to comment
Share on other sites

This is the concept behind the download script. Rather than creating a direct link instead you simply read the file as output to the user. So you link to a script that will read the file as output just like the one below. However you choose to set that up is your choice, but that is the concept.

<?php
$file=$clean_get2['file']; // please remember I use some custom cleaning scripts that I have created for my use.
$filename=explode('/',$file);
$filename=$filename[1];
$type=explode('.',$file);
$type=$type[1];
header('Content-type: application/'.$type);
header('Content-Disposition: attachment; filename="'.$filename.'"');
readfile($file);
?>

As for Madtechie's Method, this is a technique I already use, because I have seen PHP fail and choose to spit out an entire script. To prevent that my scripts consist of nothing more than includes. But the included scripts can not be accessed from the web. This is definitely a excellent way to protect your scripts and any possible information they may contain.

Link to comment
Share on other sites

I realize we've moved a little off topic so I appreciate you sticking with this.

 

Correct me if I'm wrong:

 

1) From an upload perspective since the files are above the webroot they are not accessible from the web, so I would consider that secure?

 

2)With the script in place and working correctly, the users are verified before being able to login and download and they will not have access to the folder for browsing but only through the link that is given for them to download the file?

 

I've incorporated the download script (http://www.phpfreaks.com/forums/index.php/topic,95433.0.html) and it seems to be doing its job correctly however the file that is downloaded is corrupt, any ideas on why that might be?

 

The only thing on this download.php page is PHP scripts for authorization as I've attached below.

 

<?php
//initialize the session
if (!isset($_SESSION)) {
  session_start();
}

// ** Logout the current user. **
$logoutAction = $_SERVER['PHP_SELF']."?doLogout=true";
if ((isset($_SERVER['QUERY_STRING'])) && ($_SERVER['QUERY_STRING'] != "")){
  $logoutAction .="&". htmlentities($_SERVER['QUERY_STRING']);
}

if ((isset($_GET['doLogout'])) &&($_GET['doLogout']=="true")){
  //to fully log out a visitor we need to clear the session varialbles
  $_SESSION['MM_Username'] = NULL;
  $_SESSION['MM_UserGroup'] = NULL;
  $_SESSION['PrevUrl'] = NULL;
  unset($_SESSION['MM_Username']);
  unset($_SESSION['MM_UserGroup']);
  unset($_SESSION['PrevUrl']);

  $logoutGoTo = "../index.html";
  if ($logoutGoTo) {
    header("Location: $logoutGoTo");
    exit;
  }
}
?>
<?php
if (!isset($_SESSION)) {
  session_start();
}
$MM_authorizedUsers = "";
$MM_donotCheckaccess = "true";

// *** Restrict Access To Page: Grant or deny access to this page
function isAuthorized($strUsers, $strGroups, $UserName, $UserGroup) {
  // For security, start by assuming the visitor is NOT authorized.
  $isValid = False;

  // When a visitor has logged into this site, the Session variable MM_Username set equal to their username.
  // Therefore, we know that a user is NOT logged in if that Session variable is blank.
  if (!empty($UserName)) {
    // Besides being logged in, you may restrict access to only certain users based on an ID established when they login.
    // Parse the strings into arrays.
    $arrUsers = Explode(",", $strUsers);
    $arrGroups = Explode(",", $strGroups);
    if (in_array($UserName, $arrUsers)) {
      $isValid = true;
    }
    // Or, you may restrict access to only certain users based on their username.
    if (in_array($UserGroup, $arrGroups)) {
      $isValid = true;
    }
    if (($strUsers == "") && true) {
      $isValid = true;
    }
  }
  return $isValid;
}

$MM_restrictGoTo = "../logon.php";
if (!((isset($_SESSION['MM_Username'])) && (isAuthorized("",$MM_authorizedUsers, $_SESSION['MM_Username'], $_SESSION['MM_UserGroup'])))) {
  $MM_qsChar = "?";
  $MM_referrer = $_SERVER['PHP_SELF'];
  if (strpos($MM_restrictGoTo, "?")) $MM_qsChar = "&";
  if (isset($QUERY_STRING) && strlen($QUERY_STRING) > 0)
  $MM_referrer .= "?" . $QUERY_STRING;
  $MM_restrictGoTo = $MM_restrictGoTo. $MM_qsChar . "accesscheck=" . urlencode($MM_referrer);
  header("Location: ". $MM_restrictGoTo);
  exit;
}
?>
<?php
$file = $_GET['file'];

// the absolute path to your directory storing your files.
$path = '/home/filefolder/';

$download = $file;

header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header( "Content-Disposition: attachment; filename=".$download);
header( "Content-Description: File Transfer");
header('Accept-Ranges: bytes');
header('Content-Length: ' . filesize($file));
@readfile($file);
?>

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.