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

If I wanted to allow users to edit files in one specific directory on the server (say /home/user/www-root/users/files/their_username/), is it as simple to restrict access to that one directory (as well as it's subdirectories) by testing for the following:

In this way, the filename would be passed straight from the user to the perl script (which is why I need to make sure that the user cannot edit files above their user directory. I just want to know that this is 100% safe for the server.

Here is a quick code sample of what I am planning:

#!/usr/bin/perl #Some code to read arguments passed to the script #Read STDIN (GET & POST) into %input hash if ($input{'file'} !~ /^\// && $input{'file'} !~ /\.\./) { open FILE, ">/home/user/www-root/users/files/$username/$input{'file'}" + or die "Error opening file!"; print FILE $input{'file_contents'}; close FILE; } else { #produce some error denying access to this file }

Would this suffice security wise? Thanks a lot ahead of time!

Replies are listed 'Best First'.
Re: Restricting Web Directory Access
by Zaxo (Archbishop) on Mar 01, 2002 at 06:18 UTC

    Several points to consider:

    1. You need taint mode, warnings and strict.
    2. use Basename; that strips all path information besides the file name.
    3. I hope you use CGI to read the form data.
    4. What sort of authentication is providing the user name?
    There are plenty of perils in what you want. ++For getting review, be sure to get review again before you expose this to the world. You will need to think of oversized input. Is this a file upload, or textares data written to a file?

    After Compline,
    Zaxo

Re: Restricting Web Directory Access
by mattr (Curate) on Mar 01, 2002 at 07:31 UTC
    My advice is, your architecture is insecure from the start so don't do it that way.

    You are making a fine breeding ground for remote shell exploits if you are going to let users provide parts of the path. What happens if they put a pipe mark in there? Bad things, they can execute commands possibly.

    If you really must, then perhaps you can make very sure that $username and $input{'file'} are less than say 10 characters, a..z inclusive only. Even better, scan the preset directory for files and just offer a list of files for them to select in a CGI form (see CGI.pm), instead of allowing them to create files. This way you never actually insert variables which came from the input form into your path. You also want to consider that this information is going over the network in plaintext, in fact if you use GET anyone can read it. And um, where's the password dude?

    And of course you are giving a web-based program to edit a user's root directory which is also not a good idea in general and might not work without changing permissions. Much better that your program manages everything in one place, knows what it has, and only serves things to the person who has been authenticated to get them.

Re: Restricting Web Directory Access
by jlongino (Parson) on Mar 01, 2002 at 06:10 UTC
    If you use File::Basename, filter for nasty characters (the usual taint stuff) and append the result to a path name that you've hard coded, you should be fairly safe. If I've made any glaring omissions, please correct me.

    --Jim

Re: Restricting Web Directory Access
by mt2k (Hermit) on Mar 01, 2002 at 15:16 UTC
    Okay thanks people. It seems I have a lot of work to do still! =8-)
    I like mattr's idea of restricting a..z (along with numbers and SINGLE periods.)
    Here is a good regex (or so I think/hope):

    if ($input{'file'} !~ /[^a-z0-9\.]/ && $input{'file'} !~ /\.\./) { #CODE TO WORK IT ALL OUT }

    And just to answer a couple of questions asked of me:

    • Yep, CGI reads in all my environment variables/form data
    • Username is taken from a set cookie that verifies using username/encrypted password every visit to a member-only page.
    • There are file uploads AND textarea editting. Limit already set to a smallish number
    • And I forgot to mention: all CGI, etc. is disabled in the users' directories (so no perl scripts run, etc). Access to these user directories is purely open to all users (in these directories, I set Options +Indexes in case there is no index.htm(l))
    • I understand that it would probably (okay, WOULD) be better to do this using FTP, but I don't have the privilege of doing this :)

    Any other comments???

      i wouldn't even do that. there's really no excuse for not using Taint mode if your script is opening files based on untrusted user input.

      with taint mode you would do something like:

      my $filename = ""; if($input{'file'} =~ /^(\w+\.?\w{2,4})$/) { $filename = $1; } else { die "somebody is trying to do bad things"; } # do stuff with $filename

      the idea is to only allow as much as you absolutely need to. doing anything else essentially comes down to you trying to predict how someone will attack the script and specifically blocking those attacks. not good since it means you have to know every possible attack they might try. if you miss one thing, you're screwed. taint mode encourages you to only allow as limited a set of inputs as possible.

      to be even more safe, you should probably use sysopen instead of open. if you're doing an "edit" of an existing file rather than creating a new one, you might also want to load the list of files that are in the directory into a hash and check that $input{'file'} actually matches one of them.

      but the most important thing is to use taint mode. i would suggest that before you go live with this program (however you've implemented it), you read and understand every line of the perlsec manpage. it would also be illustrative to read about the poison NULL byte for an idea of how subtle the attacks can get and why it's not good to base your security on trying to anticipate them all.

      anders pearson