Take the 2-minute tour ×
Webmasters Stack Exchange is a question and answer site for pro webmasters. It's 100% free, no registration required.

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']));   
}


?>
share|improve this question
    
This probably belongs on StackOverflow –  John Conde Aug 27 '10 at 18:35

1 Answer 1

up vote 2 down vote accepted

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.

share|improve this answer
    
This is a great explanation! Thanks. Based on your suggestion it would be better to store these files outside of the webroot that way I do not have to worry about direct link/access to the files and merely serve them up based on a php page with logic similar to above to verify the authentication of the user before serving the file. Cheers –  Chris Aug 31 '10 at 12:24
    
@John Conde I noticed content-type specific as "application/octet-stream" so this will work with any file type? This does not work, nor does specifying the content mime-type. For example I have a pdf sitting there that I can pull and know is a good pdf but when I serve it up via fashion you present, I receive "File type plain text document (text/plain) is not supported". –  Chris Aug 31 '10 at 15:17
    
I believe so. If it doesn't then all you need to do is change that part to the proper mime type. But I'm pretty sure that will work for any file type. –  John Conde Aug 31 '10 at 15:21
    
For some reason, I can goto uploads/filename.pdf but the php script cannot serve the file up using either application/octet-stream or application/pdf for this specific case. –  Chris Aug 31 '10 at 15:25
    
What error do you get? Verify that the script can find the file as the path may be incorrect somewhere. –  John Conde Aug 31 '10 at 15:45

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.