8

I have a system (web based application) which pulls file attachments from a 3rd party system via SOAP. These are in turn on our system created as files in a directory.

When a user of the system (authenticated through ldap) makes a request to my application to retrieve one of these attachments:

1. I request it via soap
2. Process the response to build the file on our system
3. Redirect user to the location so they can download the file.  

First off, is this a good approach?

Is there a better way to serve up files which will not reside on the server much after the attachment download (cron job will clean up the directory every so often)?

Secondly, is there a way I can serve files through apache without storing them in the web root?

Thirdly, how do I enforce permissions on these files so not just any user can download just any attachment?

Our Setup:

linux
apache
php - soap libraries for communication 
seperate LDAP for authentication
3rd party soap server (where attachments come from) 

EDIT: The code to serve the attachment in case anyone is curious.

    <?php 

ini_set('display_errors',1);
error_reporting(E_ALL|E_STRICT);

//require global definitions 
require_once("includes/globals.php"); 
//validate the user before continuing 
isValidUser(); 
$subTitle = "Attachment";   
$attachmentPath = "/var/www/html/DEVELOPMENT/serviceNow/selfService/uploads/";
if(isset($_GET['id']) and !empty($_GET['id'])){
    //first lookup attachment meta information 
    $a = new Attachment(); 
    $attachment = $a->get($_GET['id']); 
    //filename will be original file name with user name.n prepended 
    $fileName = $attachmentPath.$_SESSION['nameN'].'-'.$attachment->file_name; 
    //instantiate new attachmentDownload and query for attachment chunks 
    $a = new AttachmentDownload(); 
    $chunks= $a->getRecords(array('sys_attachment'=>$_GET['id'], '__order_by'=>'position')); 


    $fh = fopen($fileName.'.gz','w');                                                      
    // read and base64 encode file contents 
    foreach($chunks as $chunk){
            fwrite($fh, base64_decode($chunk->data));   
    }
    fclose($fh);

    //open up filename for writing 
    $fh = fopen($fileName,'w');     
    //open up filename.gz for extraction                                
    $zd = gzopen($fileName.'.gz', "r");
    //iterate over file and write contents 
    while (!feof($zd)) {
            fwrite($fh, gzread($zd, 60*57));    
    }
    fclose($fh); 
    gzclose($zd);
    unlink($fileName.'.gz'); 
    $info = pathinfo($fileName); 

    header('Content-Description: File Transfer');
    header('Content-Type: '.Mimetypes::get($info['extension']));
    header('Content-Disposition: attachment; filename=' . basename($fileName));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . filesize($fileName));
    ob_clean();
    flush();
    readfile($fileName);
    exit();
}else{
    header("location: ".$links['status']."?".urlencode("item=incident&action=view&status=-1&place=".$links['home']));   
}


?>
Simon Hayter
  • 33,097
  • 7
  • 60
  • 119
Chris
  • 339
  • 1
  • 10

1 Answers1

2

First off, is this a good approach?

Sound fine to me. Just make sure you authenticate the user before going through all of that.

Is there a better way to serve up files which will not reside on the server much after the attachment download (cron job will clean up the directory every so often)?

Secondly, is there a way I can serve files through apache without storing them in the web root?

Put the files outside of the webroot. Then using PHP pass the file though a script. That way no one can link to the file directly and bypass your controls. (Naturally make sure the script that does this only after verifying the user has permission to retrieve that file).

Sample PHP:

<?php
    if (!isset($_SESSION['authenticated']))
    {
        exit;
    }
    $file = '/path/to/file/outside/www/secret.pdf';

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename=' . basename($file));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    readfile($file);
    exit;
?>

Thirdly, how do I enforce permissions on these files so not just any user can download just any attachment?

Make the users log in to retrieve their files. Then you can set a session variable identifying them as being allowed to download. Make sure your scripts authenticate them on every page of this process.

John Conde
  • 86,484
  • 28
  • 150
  • 244