Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

file download security

by lonewolf32 (Initiate)
on Apr 15, 2002 at 14:57 UTC ( [id://159218]=perlquestion: print w/replies, xml ) Need Help??

lonewolf32 has asked for the wisdom of the Perl Monks concerning the following question:

I am working with Perl and NT Server. I am trying to set up a "simple" and secure way for users to download secure zip files. I already have a javascript front end for them to log in using a preassigned u/p. After logging in it forwards them to a page listing files they are allowed to download. This page should be secure, i.e. they should only be able to get to it thru the login page. Also the files this page links to need to be secure. I found a code snippet on this site I could use to download files, something like:
my $filepath = "<path here>"; my $filename = $ENV{'QUERY_STRING'}; my $filesize = -s "$filepath/$filename"; print "Content-disposition: attachment; filename=$filename\n"; print "Content-Length: $filesize\n"; print "Content-Type: application/octet-stream\n\n"; my $buffer; open FILE, "$filepath/$filename" or die "Oops $!"; binmode FILE; binmode STDOUT; print $buffer while (read(FILE, $buffer, 4096)); close FILE;
This is the whole contents of a file I have called test.pl. I am new at this so please bear with me. I see how I can place the files to download in a non-public directory on the server, but how do I prevent a user from typing "http://website.com/cgi-bin/test.pl" right in his browser address bar? This would just download the file for them. If I change permissions on the server for my cgi-bin directory then the calling file can't access test.pl either. Thanks - Dave

Replies are listed 'Best First'.
Re: file download security
by Ovid (Cardinal) on Apr 15, 2002 at 15:34 UTC

    Warning: You have a whopping security hole in your script. Because you don't check that the user-supplied filename is safe, the user could use this script to open (and possible run) any file on your box that the script would have the rights to access.

    As for your original question, you have a few options. The easiest is to allow basic authentication and require each user to login in before they can get to your cgi-bin. However, basic authentication sends the data "Base64" encoded, which is plain-text. If you need this secure, this is not a good solution.

    You could build a simple password authentication application that controls access to the other applications, but that also won't be terribly secure unless you use SSL, which is really the only way you're going to get decent security. Incidentally, my cgi course has information for a simple authentication program in Lesson 4, part 2. In fact, after looking at your code, I think there are a few other pointers you might appreciate from that course (no offense!).

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      Isn't the fact that we have:

      open(FILE "$filename")

      another big one? (I'm getting back into Perl after a year of two of Java)

      open(FILE "<$filename")

      is a good idea to ensure that even if the user is able to access files that they shouldn't using this script, at least they can't replace foo.txt with my_evil_virus.txt.

      HTH

        That is why the code is:

        open FILE, "$filepath/$filename"; # so provided we hard code $filepath.... my $filepath = '/usr/somewhere'; # and untaint $filename ensuring there are no ../ etc, in it my $filename = $q->param('filename') || ''; my ($filename) = $filename =~ m/^([\w.-]+)\z/; # then this is quite safe... open FILE, "$filepath/$filename" or die $!;

        As you rightly point out open FILE, $file where the user supplies $file and it is not untainted is dangerous as hell, see this for why

        cheers

        tachyon

        s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

        BTW the hard coded < provides no protection. Beside the obvious fact that we only read from the file - not print to it consider $filename = 'ls; cd /; rm -rf *'

        You can satisfy the < easily with say ls then add a ; then go for your life.... The keys for security are 1) hard code the path; 2) untaint the filename so it can only contain m/^[A-Za-z._-]+\z/ which stops the old ../../../etc/passwd Setting taint mode with the -T flag will catch a lot of errors. Don't CGI without it.

        cheers

        tachyon

        s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: file download security
by perlplexer (Hermit) on Apr 15, 2002 at 15:32 UTC
    This page should be secure, i.e. they should only be able to get to it thru the login page

    How secure do you want to be? Even if you have a login page, user name and password will be sent as plain text over the network. So, there is always a possibilty that someone will get them. One way of making that significantly harder for people to do is by using HTTPS. You can refer to your web server documentation on how to set up HTTPS.

    how do I prevent a user from typing "http://website.com/cgi-bin/test.pl" right in his browser address bar

    Every time a user logs in, create a unique session ID and then pass it on throughout the site as one of the parameters; e.g., file.cgi?sid=foobar
    Once you are able to do that, then, inside of your file.cgi script, you can check if "sid" is present and valid (not expired). If it doesn't exist or not valid then you can redirect the user to some other page.

    Hope this helps.

    --perlplexer
Re: file download security
by extremely (Priest) on Apr 15, 2002 at 15:35 UTC
    I'm boggled by this. Is there some reason why you can't turn on password protection in the webserver? I'm pretty sure they invented that for things like this. There isn't much in the way of gymnastics you can do to fix this.

    My best suggestion would be that you let them select which file they want at the same time as they are offered the password login. Then, have the download script check that they have asked for a legitimate file AND that they have entered the password. If they don't give you any post/get data, redirect them to the front page and if they give you bad data show them an error. I think that is the best you are going to do.

    And if you really are checking the password in javascript and you dared utter "secure" in the same breath, I'm never speaking to you again. =) =)

    --
    $you = new YOU;
    honk() if $you->love(perl)

      I know - this won't be terribly secure. We are a small "niche" company, few ordinary people would want to download our stuff. I just want to get past the obvious security holes that a casual user would be tempted by.
        Welll, OK. *eyes you suspiciously* =)

        I still don't understand why you don't just turn on some sort of webserver authorization for the directory you place the files in. That would solve your problem with exactly 0 lines of code...

        --
        $you = new YOU;
        honk() if $you->love(perl)

Re: Sessions and file download security
by tachyon (Chancellor) on Apr 15, 2002 at 19:57 UTC

    The easiest way to do basic authentication is via the .htaccess file but this is a part of Apache web server (the *nix webserver). As you are on NT you will be using IIS I expect. IIS does not have the same functionality.

    Here is a thread on it

    A Google Search for 'IIS and .htaccess' will turn up lots more info.

    You can do everything you want in one cgi script but it is very non-trivial. Like .htaccess the username and password do hit the internet in plain text where they can be got by packet sniffers. However a script lets you have much more control over what files each user can see. With .htaccess style access you can either read a directory or you can't.

Re: file download security
by Molt (Chaplain) on Apr 15, 2002 at 15:09 UTC

    Have you tried checking the HTTP referer is the page that they should be coming from? It's been a while since I've done CGI so I can't recall the exact name of the variable, but I know it's somewhere in %ENV so if you dump that, or hit the docs, you should find it easily enough.

    If this isn't sufficient security, and you're using JS and suchlike already, then maybe some cookie-based system that puts a short-expiration cookie on their machine on the entry page, and then tests it's existance on the download?

    Either way if the test fails it's probably a good idea to direct them back to the entry page, but also add a message saying why they're back at the entry page and a 'Contact us with download problems' link just in case something is wrong with your tests. Don't rely on the browser to do the right thing, it never will.

      You may not be aware of this, but the HTTP referer, like the IP address, can be spoofed. Using this as a method of security is a bad idea as it is trivial to build an application (or use pre-made "script kiddie" tools) that fakes this information for you.

      Cheers,
      Ovid

      Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      It's been a while since I've done CGI so I can't recall the exact name of the variable

      $ENV{HTTP_REFERER} (note the spelling) will contain the referrer.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://159218]
Approved by cjf
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (6)
As of 2024-03-28 14:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found