7

My component will produce invoices in PDF format that will be available for customers to download.

Where is the best place to store these files?

/adminitrator/componentes/com_mycomponent/invoices

/componentes/com_mycomponent/invoices

/media/com_mycomponent/invoices

How can I prevent unauthorised users just directly downloading these files?

How can I prevent customers from downloading other people's invoices?

7

I would advise against using

administrator/components/com_mycomponent/invoices 

This is because it could pose a problem when you use .htaccess on your administrator directory. For example, with Akeeba Admin Tools

I would prefer using the media folder as well.

You can also serve the hashed files with their proper original names like this:

<?php
header("Content-type:application/pdf"); 

// It will be called NICE_TITLE.pdf
header("Content-Disposition:attachment;filename='NICE_TITLE.pdf'");

// The PDF source is in HASHED_FILE.pdf
readfile("HASHED_FILE.pdf");
?>
6

This may not answer your question directly, but I wanted to give another point of view on this question.

I would try to avoid as much as possible storing documents that you can generate. Reason: you need to decide on folder names / file name, which later are hard to change. It may use too much space and could pose security issues.

Of course this depends on how your application works, but ideally I would have the invoice data in database tables and generate invoices on the fly, when needed.

My two cents.

  • 2
    Generate them on the fly << great idea – Lodder Jun 3 '15 at 7:26
  • As a related consideration: if you pull information dynamically that may change (e.g. using a user's name) the 'static' PDF allows you to have a point-in-time record rather than taking changes. Currently we're working on an e-learning system, and the certificates we generate have to have their name at the time they passed (so people can simply change their name, then generate a new certificate for their friend who hasn't passed). Just a thought. – codinghands Jun 3 '15 at 9:18
  • @codinghands - In which case you would associate the name of the PDF in the database with the users id rather than name. Upon generation, you would then match the id in the table against the id in the #__users table to get their username or full name ;) Overall I much prefer this method as it saves a hellish amount of server space – Lodder Jun 3 '15 at 10:09
  • If the name needs to be printed on the PDF, it doesn't matter what you link the PDF to in the DB - if the name changes, so will the certificate (if it's dynamic), which in the use case I describe is bad times. – codinghands Jun 3 '15 at 10:17
  • The name will not change because you will hard code it in the invoice table for the reason you mentioned. The invoice should not use table associations for such information. – Valentin Despa Jun 3 '15 at 10:47
5

Just thought I'd throw in my 2 cents (some good points in hashing the filename).

I recently had a similar issue. Instead of worrying about .htaccess, I just save the files above public_html, with a component parameter specifying the path, then using PHP's readfile to fetch and output the PDF.

Won't work with all hosts, but for most applications it's relatively low-effort and completely prevents any hotlinking or other access.

3

Never store any user generated files in the component directories. You may end up loosing them during an update of your component (if not done properly) or for sure when the user uninstalls it.

The correct place to store such files is actually the "images" folder. It's the place where the user will be able to manage the files easily using the media manager. The "media" folder would be another viable choice, but it is actually more meant to hold extension assets like CSS, JS and the like.

However if the files contain sensible data which should not be available to anyone, then storing them in any of this folders is a bad idea. Even if you hash them you can be sure that someone eventually will be able to read them.

If it needs to be secure, either don't store the file at all and generate it instead dynamically on request. Or store it outside of webroot and access it using PHP. There is no other secure way.

  • You may end up loosing them during an update of your component >> I don't believe files stored in a directory will be removed unless you specify that in the update script. Agree with the uninstall though – Lodder Jun 3 '15 at 10:13
  • 1
    It will automatically delete folders during an update if you had them specified in your manifest XML in one release and in a later release removed it. – Bakual Jun 3 '15 at 12:36
2

Using the following directory as a root for all invoices is perfectly fine:

administrator/components/com_mycomponent/invoices

However, what I would recommend doing is hashing the file name once the invoice is generated. Like so:

administrator/components/com_mycomponent/invoices/HASHED_FILE.pdf

Either that or, for each invoice, create a hashed directory and store the PDF in there, like so:

administrator/components/com_mycomponent/invoices/HASHED_DIR/file.pdf

Update:

If this method is chosen, you can use a htaccess file to prevent direct access and only allow PHP driven access to these PDF files.

For a PHP driven download, I would suggest storing the hash in a database tabling, along with the user's ID. When a request is made to download the file, you can then match the ID you've stored with the ID of the logged current logged in user, and of course, match the hash stored in the database.

  • @downvoter, step up, show yourself and explain why you think what I've written is wrong – Lodder Jun 3 '15 at 8:08
  • Please don't store user generated files in components directories. Also hashing alone will not make it safe to access. It's only a bit harder. If it needs access restrictions, it needs to be restricted, not just veiled. It would give a false sense of security. – Bakual Jun 3 '15 at 12:45
  • @Bakual - You can use a htaccess file to prevent direct access to a file and only allow it to be access via PHP – Lodder Jun 3 '15 at 12:49
  • It would still be the wrong place to store them within the components directories. Use images, media or a folder outside of webroot. – Bakual Jun 3 '15 at 13:14
2

To me its media folder best fit for that purpose. To restrict direct access, just create .htaccess file in your folder with the below content.

DENY FROM ALL
  • Any reason for downvote? please comment if anything wrong with the answer.. – Nagarjun Jun 3 '15 at 7:53
  • Because "any of the places" is wrong. Files should not be stored in the components directories. – Bakual Jun 3 '15 at 12:46
  • How that should be wrong? Is there any rule saying that? It is not recommended to store files in component directories as they will get overweritten if the update packages have same files otherwise there will be no issue with it. Unless you uninstall the component, there wont be any issue with the component directories. And I clearly stated my preference as media folder. – Nagarjun Jun 3 '15 at 13:00
  • It's explained in the "best practice" here: docs.joomla.org/Development_Best_Practices – Bakual Jun 3 '15 at 13:19
  • Well.. the name of the document explains what I meant above. Thanks for the link anyway. – Nagarjun Jun 3 '15 at 13:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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