SitePoint Sponsor

User Tag List

Results 1 to 18 of 18
  1. #1
    SitePoint Member
    Join Date
    Feb 2001
    Location
    Dumont, NJ
    Posts
    11
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi. I've got an intranet and I have .xls, .doc, and .pdf files I want to make available to my students. Problem is, once someone gets the url to a file anyone can use it to download the file.
    What can I do so that the downloaded file's url can't be seen or the visitor is screened as a valid user via a cookie? Using the latter method I would have to change to file_name.php3 which changes everything and I'm not familiar enough with playing with the URL to solve the problem. Any ideas?
    Thanks.

  2. #2
    SitePoint Author Kevin Yank's Avatar
    Join Date
    Apr 2000
    Location
    Melbourne, Australia
    Posts
    2,571
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    This should give you the idea.

    See my article, Managing Users with PHP Sessions and MySQL for the user authentication code contained in accesscontrol.php, or just use a simple username/password authentication scheme. The list of MIME types is far from complete, so add your own as needed.

    PHP Code:
    <?php
    include('accesscontrol.php');

    if (!isset(
    $filename)) {
        
    ?>
        <HTML>
        <HEAD><TITLE>Error</TITLE></HEAD>
        <BODY>
        <P><B>Script Error:</B> No file was specified to
              download. Please report this error to <A
              HREF="mailto:yournamr@yourdomain.com">
              [email]yourname@yourdomain.com[/email]</A>.</P>
        </BODY>
        </HTML>
        <?php
        
    exit();
    }

    if (
    ereg("^.+\\.(.+)$",basename($filename),$match)) {
        
    $fileExtension strtolower($match[1]);
    }
    switch (
    $fileExtension) {
    case 
    "gif":
        
    $contentType="image/gif";
        break;
    case 
    "jpeg":
    case 
    "jpe":
    case 
    "jpg":
        
    $contentType="image/jpeg";
        break;
    case 
    "pdf":
        
    $contentType="application/pdf";
        break;
    case 
    "doc":
        
    $contentType="application/msword";
        break;
    case 
    "ppt":
        
    $contentType="application/powerpoint";
        break;
    case 
    "ps":
    case 
    "eps":
    case 
    "ai":
        
    $contentType="application/postscript";
        break;
    case 
    "zip":
        
    $contentType="application/x-zip-compressed";
        break;
    case 
    "gz":
        
    $contentType="application/x-gzip";
        break;
    case 
    "bat":
    case 
    "txt":
    case 
    "c":
    case 
    "h":
    case 
    "cpp":
    case 
    "hpp":
        
    $contentType="text/plain";
        break;
    default:
        
    $contentType="application/octet-stream";
    }
    header("Content-Type: $contentType");
    header("content-disposition: filename=".basename($filename));
    header("content-length: ".filesize("/path/to/files/".basename($filename)));

    readfile("/path/to/files/".basename($filename));
    ?>
    Last edited by Kevin Yank; Apr 6, 2001 at 20:16.
    Kevin Yank
    CTO, sitepoint.com
    I wrote: Simply JavaScript | BYO PHP/MySQL | Tech Times | Editize
    Baby’s got back—a hard back, that is: The Ultimate CSS Reference

  3. #3
    SitePoint Member
    Join Date
    Feb 2001
    Location
    Dumont, NJ
    Posts
    11
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thank you, Kevin.
    I haven't had a chance to try your script yet, so let me paraphrase what it suggests to me. When sent a file name and the path to the file, your script, I'll call it download.php, will create an appropriate url that instructs the visitor's browser to download the desired file. After authenticating the visitor of course!. Sweet. Thanks again!

  4. #4
    SitePoint Author Kevin Yank's Avatar
    Join Date
    Apr 2000
    Location
    Melbourne, Australia
    Posts
    2,571
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    That's not really what happens. Rather, if accesscontrol.php verifies that a valid username/password have been provided, then in response to the browser's request for download.php, the requested file will be sent, along with the HTTP headers that tell the browser what filename to use when saving the file to the user's hard drive.

    In other words, download.php doesn't forward the user's browser to the URL to download the file, download.php actually sends the requested file itself.
    Kevin Yank
    CTO, sitepoint.com
    I wrote: Simply JavaScript | BYO PHP/MySQL | Tech Times | Editize
    Baby’s got back—a hard back, that is: The Ultimate CSS Reference

  5. #5
    Don't eat yellow snow spaceman's Avatar
    Join Date
    Mar 2001
    Location
    Melbourne, Australia
    Posts
    1,039
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've just posted a similar problem/question on a different web site's PHP forum before coming here and seeing this thread.

    kyank - correct me if I'm wrong, but your code will successfully mask the directory location (and hence the URL) of the file being downloaded. But if anyone on the planet (whether authenticated or not) was to enter the correct URL of the 'private' file, then your script in no way prevents that file from being downloaded.

    I can't afford this 'security by obscurity' solution in the application I wish to build. I need to to know that even if a non-authenticated user enters a correct URL for a document which is supposed to be 'private', access will be denied.

    The apache module mod_auth-mysql would be a solution, but my current hosting provider won't install this on their shared server.

    Any comments or guidance would be appreciated.

  6. #6
    ComDude CryingWolf's Avatar
    Join Date
    Dec 2000
    Location
    I don't know the cat drug it in!!!
    Posts
    247
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok just a newbie asking dumb questions so ignore me as a goof if I am way wrong here.

    But couldn't you set up a directory with a .htaccess file and protect those files from just anyone? Wouldn't this also stop access even if they had the direct url cuz it would ask for a login password?

    Again I am just starting to play with this so just tell me to sitdown and shutup if I am totally wrong???

    Late
    body { background:#000000; color:#000000 }
    HEY, WHO TURNED OUT THE LIGHTS?!?
    Easy come easy go!!!
    CryingWolf

  7. #7
    Don't eat yellow snow spaceman's Avatar
    Join Date
    Mar 2001
    Location
    Melbourne, Australia
    Posts
    1,039
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi CryingWolf,

    The problem with setting up .htaccess and .htpasswd files is that these files are relatively static in nature. I have multiple users logging in using their own username/password pairs. And they are allowed to edit their own passwords whenever they like. I will be storing these usernames and password in a MySQL database, but unless at the same time I am dynamically updating the .htpasswd file to reflect the current list of users and their passwords, then the simple .htaccess/.htpasswd solution limits me to telling my users that the username/password pair to access the private document directory is, for example, 'foo' and 'bar'. This would work, but it wouldn't be the dynamic solution I am looking for.

    Hope that helps.

  8. #8
    ********* Callithumpian silver trophy freakysid's Avatar
    Join Date
    Jun 2000
    Location
    Sydney, Australia
    Posts
    3,798
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm talking through my hat here (as usual) because I don't really have any experience of using .htaccess and .htpasswd files for user authentication - but don't forget that there is no reason why these files can't be seen as dynamic too. Sure reading, writing and updating records in a flat file is a bit more of a pain in the bum to code compared to embedded sql - but is very do-able. After all, at the end of the day, a database is just a few files too. If you only have a limited number of users (not hundreds or thousands) and concurrenty control is not likely to be a big problem - then I can't see why you couldn't manage user access thru these files. Just thinking out aloud.

  9. #9
    SitePoint Author Kevin Yank's Avatar
    Join Date
    Apr 2000
    Location
    Melbourne, Australia
    Posts
    2,571
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Originally posted by spaceman
    kyank - correct me if I'm wrong, but your code will successfully mask the directory location (and hence the URL) of the file being downloaded. But if anyone on the planet (whether authenticated or not) was to enter the correct URL of the 'private' file, then your script in no way prevents that file from being downloaded.
    If the files are stored in a directory outside of your Web document directory structure, then the only way those files may be accessed is through this script.

    In other words, place the files somewhere on the server where no URL exists to access them! Thus, the only way to access the files is through the download script, which requires a correct username and password (stored in a dynamic database and verified by accesscontrol.php) to allow the download to proceed.
    Kevin Yank
    CTO, sitepoint.com
    I wrote: Simply JavaScript | BYO PHP/MySQL | Tech Times | Editize
    Baby’s got back—a hard back, that is: The Ultimate CSS Reference

  10. #10
    SitePoint Zealot moshe_be's Avatar
    Join Date
    Dec 2000
    Posts
    169
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Or you can put on the folder that got the documents .htaccess file with the following lines:

    <Limit GET>
    order allow,deny
    deny from all
    </Limit>

    That would not allow it to be viewed from the web.

    P.S. My site does not have folder which is non-visible from the web, so this is good solution.

  11. #11
    Don't eat yellow snow spaceman's Avatar
    Join Date
    Mar 2001
    Location
    Melbourne, Australia
    Posts
    1,039
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    moshe_be - ok, so you deny direct web access to the directory containing the private documents, and then you allow access to authenticated users by providing a hyperlink to the documents in question from within the member/intranet area - correct? But when an authenticated users clicks on one of the hyperlinks, doesn't the .htaccess file block this access?

    I like kyank's solution too - I've been chatting with my own hosting provider and have learn that in order to read/write to directories outside of the www hierarchy I have to run my PHP scripts as cgi scripts out of my cgi-bin directory.

  12. #12
    SitePoint Zealot moshe_be's Avatar
    Join Date
    Dec 2000
    Posts
    169
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Nope, no one can access that folder from the internet, it is good for Kyank's solution. It does allow access via PHP script by the readfile function.

  13. #13
    ComDude CryingWolf's Avatar
    Join Date
    Dec 2000
    Location
    I don't know the cat drug it in!!!
    Posts
    247
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok I am on board now even though I don't know anything about php yet. But I do get the idea behind it. Good descussion thanks for giving me something to think about as I sortof wanted to do the same thing (protect files but have them available for some ppl)



    Late
    body { background:#000000; color:#000000 }
    HEY, WHO TURNED OUT THE LIGHTS?!?
    Easy come easy go!!!
    CryingWolf

  14. #14
    SitePoint Author Kevin Yank's Avatar
    Join Date
    Apr 2000
    Location
    Melbourne, Australia
    Posts
    2,571
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    I've been chatting with my own hosting provider and have learn that in order to read/write to directories outside of the www hierarchy I have to run my PHP scripts as cgi scripts out of my cgi-bin directory.
    Sounds like your host is running PHP in safe mode. This is a pain, but is an unfortunate reality on many hosts.
    Kevin Yank
    CTO, sitepoint.com
    I wrote: Simply JavaScript | BYO PHP/MySQL | Tech Times | Editize
    Baby’s got back—a hard back, that is: The Ultimate CSS Reference

  15. #15
    SitePoint Member
    Join Date
    Feb 2001
    Location
    Dumont, NJ
    Posts
    11
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I just wanted to report that I used Kevin's script and it worked exactly as advertised. The user has no idea where the downloaded file is coming from because the URL doesn't change in the browser. Very slick.
    Thanks again Kevin.

  16. #16
    SitePoint Member
    Join Date
    Feb 2001
    Location
    Dumont, NJ
    Posts
    11
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I spoke too soon and I think I'm sunk.

    The script that Kevin Yank provided in this thread works when coming from a static page but it doesn't work if the page with the reference was generated by php. At least that is how it seems to me.

    I have a php script that will generate a list of links. One of the links might be:

    <li><a href="securedownload.php3?filename=ex.xls">download the spreadsheet example</a><br>

    This link works fine in a static .html page, however, when the page is generated by php following this link will return the following error:

    Warning: Cannot add more header information - the header was already sent (header information may be added only before any output is generated from the script
    - check for text or whitespace outside PHP tags, or calls to functions that output text) in /blah/blah/blah/securedownload.php3 on line 67.

    This is repeated once for each header() command in the script.

    Obviously, I'm breaking a rule here. What can I do? Am I doing something wrong? Is there any workaround?

    I suppose I could put the .xls file in a mysql blob column and use a readfile command but.. I would rather have this work.

    Thanks everyone.

  17. #17
    SitePoint Author Kevin Yank's Avatar
    Join Date
    Apr 2000
    Location
    Melbourne, Australia
    Posts
    2,571
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    It shouldn't matter how the page containing the link was generated. An HTML link is an HTML link -- the download script shouldn't know the difference.

    Can you put up an example somplace that I can look at so I can tell you what's wrong?
    Kevin Yank
    CTO, sitepoint.com
    I wrote: Simply JavaScript | BYO PHP/MySQL | Tech Times | Editize
    Baby’s got back—a hard back, that is: The Ultimate CSS Reference

  18. #18
    Don't eat yellow snow spaceman's Avatar
    Join Date
    Mar 2001
    Location
    Melbourne, Australia
    Posts
    1,039
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    storing files in a MySQL database

    Thinking outside the square, another suggestion I have received regarding the whole private file access issue is the option to store the files (.xls, .jpg, .doc, whatever) within a MySQL database/table (in a LONGBLOB field).

    I've haven't tried this myself, and I'm quite sure that storing such files in this manner is nowhere near as efficient as storing the files 'as-is' in the directory structure, but it seems to me that this method of managing uploaded files has the following two benefits:
    1. Removes the need to CHMOD 777 the directory where you want to store the files
    2. Allows the programmer to more easily and securely control access to appropriate, logged-in users.

    Comments anyone?

    If you're interested, this article should get you started: http://www.phpbuilder.net/columns/florian19991014.php3
    Last edited by spaceman; Apr 19, 2001 at 02:52.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •