Re: Using Variables in Path Names
by Fastolfe (Vicar) on Nov 28, 2001 at 03:22 UTC
|
Danger!
Your script makes some very classic mistakes and has some very serious security vulnerabilities that could allow any person to write files anywhere on your system.
Some things to look at:
To literally answer the question you're asking, yes, you can put variables in a double-quoted string like that. It should do what you're expecting. So the problem must lie in how those variables are assigned. Some debugging code (e.g. print statements here and there) to check that each variable has the value you're expecting, and that each directory exists as you expect, etc., would help in your debugging efforts.
But I wouldn't use this logic as it is at all without doing some serious sanitizing of input and/or verification of each parameter they're passing (e.g. ensuring that the class is a valid class name, that the user is a valid user, etc.). Anyone can put "../../../" in any of those variables and back-track their way out of your filesystem.
| [reply] |
Re: Using Variables in Path Names
by tadman (Prior) on Nov 28, 2001 at 03:27 UTC
|
Before you get assaulted by the CGI.pm police (Update: too late! :-), here's what
you should really be doing up front:
#!/usr/local/bin/perl -wT
use strict;
use CGI;
my $q = CGI->new();
print $q->header();
# Using CGI is even easier than doing it yourself,
# so PLEASE(!) use it!
$studentid = $q->param('studentid');
$CSC = $q->param('CSC');
chomp($CSC);
$assignment = $q->param('assignment');
$file = $q->param('file');
Now, consider constructing a path from variables, such as you are trying to do. Instead of using string interpolation, such as "$HOME/$x/$y", you should just join:
my $path = join ('/',
$HOME, 'classes', $CSC,
$studentid, $assignment,
'outputfile.txt');
Of course, before you even think of doing this, you must validate your parameters to make sure they are "kosher". Using 'perl' with the '-T' parameter makes user
data tainted, or icky, and your program will fail with
errors unless you check them out first.
# An example of "validated" input
my ($studentid) = $q->param('studentid') =~ /(\w+)/;
You should define your input specification as narrow as
possible. For example, if you just wanted numbers, you can
use '\d+'. If none of this makes any sense, a quick browse through the regular expressions reference will
help immensely. This is time well spent.
You should note that CGI.pm helped you by:
- Not having to write your own "text/html" header.
- Not having to test for various methods (POST vs. GET)
- Not having to worry about de-mangling %-ified parameters
- Not having to handle more than one instance of the same
parameter name (i.e. a "CHECKBOX" with one-or-more selections possible)
| [reply] [d/l] [select] |
|
Hi, tadman. You say,
"Instead of using string interpolation,
such as "$HOME/$x/$y", you should just join".
Next to doing dumb things, I hate doing wise things without
knowing why.** And in this case I can't think of a reason
why join would be better for this.
What is the reason for your assertion?
---------------------
**I've heard this called "Cargo Cult Correctness", i.e.
(blindly) doing the right thing but not knowing the reason.
;-)
| [reply] |
|
I think tadman ment 2 things. First of all, with the join method you only have a hard-coded separator in one place. If you want another, just replace that one, or better, define a variable for that.
Two, you'd better assign variables, or arrays, to define the
subdirectories node for node. That way, the code gets more readible (less comments needed) and better maintainable.
Jeroen
| [reply] |
|
|
|
|
|
| [reply] |
Re: Using Variables in Path Names
by premchai21 (Curate) on Nov 28, 2001 at 03:22 UTC
|
A suggestion: use strict warnings and diagnostics or die; it also would be a good idea to use the module CGI, as its parser is considered the best for query strings; note also that your if statements can be combined. I'm not sure, but I believe it's the roll-your-own parser that's causing you grief. Also look at perlsec. Try using CGI, strict, and warnings (or -w), as well as possibly taint mode (perlrun / perlsec) then come back if you still have problems.
| [reply] |
Re: Using Variables in Path Names
by fuzzysteve (Beadle) on Nov 28, 2001 at 03:52 UTC
|
don't have a solution for you, but i do have a suggestion.
replace
open( OUTFILE, ">>$filename" ) or die ( "no go miss ");
with
open( OUTFILE, ">>$filename" ) or die ( "no go miss: $!");
That way you can get an error messager that might help you track down the problem.
Thought that comes to mind. how does perl deal with appending to a file that doesn't exist? I'd have thought it just created it, but I'm not sure.
| [reply] [d/l] |
|
Hi,
Thought that comes to mind. how does perl deal with appending to a file that doesn't exist? I'd have thought it just created it, but I'm not sure.
From perldoc -f open:
If the filename begins with '>>', the file is opened for appending, again being created if necessary.
I was curious too and just thought I'd share my findings.
Quinn Slack
perl -e 's ssfhjok qupyrqs&&tr sfjkohpyuqrspitnrapj"hs&&eval&&s&&&'
| [reply] |
Re: Using Variables in Path Names
by Dogma (Pilgrim) on Nov 28, 2001 at 05:33 UTC
|
There are already several very could posts on this topic. Even some pointing out that you should be using taint checking and sanitizing your data. In addition to that authorizing access of this nature is extremely important. I thought I might help by adding why this is so important. If I were a mean person (not saying I'm not) but for the moment lets say I'm intent on causing trouble. Lets say I submit my assignment not as Laura but as Bob. I don't like Bob much, he sits next to me in class and breathes with his mouth open. So I decide to call my outputfile .bash_rc or .bash_profile. Even with a path appended to the string I can just make my file name ../../../.bash_rc or whatever it takes to get down to the home dir. Then I make the body of my assignment "rm -rf . > /dev/null &". As you can see Bob is going to have a "Very Bad Day{TM}" on his next loggin. Now while your code doesn't allow this in it's current form something workable probably would allow something similar. I'm just trying to illustrate how many security wholes you open up when you allow code to run that's dependant on html form fields. If he had to go through some sort of authorization it would lessen the chances of Laura getting even with evil mouth breathers on the system. I hope this puts some fear into you. On and I do speak from experience I used to be the only Unix Admin for a Computer Science department with 600 active accounts each quarter. | [reply] |
Re: Using Variables in Path Names
by andye (Curate) on Nov 28, 2001 at 15:44 UTC
|
Hi lfindle, just a quick note in addition to all the useful advice above.
One alternative (to constructing the pathname by untainting user-supplied parameters) would be to match the incoming parameters against a hash of allowable values. Obviously this only works if there are a fixed set of allowable values.
So, say that the incoming param is called 'dir', and you don't want to allow anything except 'a','b' or 'c', you could do: my %allowed_dirs = (a => 'a', b => 'b', c => 'c');
my $dir = $allowed_dirs{$q->param('dir')};
die "go away, you nasty horrid hacker" unless defined $dir;
Then you can interpolate $dir into your path in the certain knowledge that Everything Is All Right.
hth, andy. | [reply] [d/l] |
Re: Using Variables in Path Names
by Aighearach (Initiate) on Nov 28, 2001 at 11:00 UTC
|
While I'm not a member of the CGI police (but you should look into CGI.pm itself, and compare your query grab with it's), I do have a comment.
my $file = $FORM{file};
$file =~ s{.*/}{}; # remove any path, this takes care of the ../.. sec
+urity problem
$file =~ s/[^\w\-.]//g; # remove anything in the filename that isn't a
+ word character, a dash, or a dot. This takes care of the `rm -rf /`
+type threats.
...
My view on the CGI.pm fascists is, YES you should use CGI.pm until you understand what the differences are. But including bloated modules in your code may, or may not, be a good idea. It depends, as things do, on context. For your usage, probably CGI.pm is a good idea. But if you were optimizing to keep the webservers memory usage low under heavy load, then CGI.pm might not be as good.
-- Snazzy tagline here
| [reply] [d/l] |
Re: Using Variables in Path Names
by lfindle (Acolyte) on Nov 29, 2001 at 03:56 UTC
|
First things first...Thank you very much to everyone who responded. I'm still in the process of reading over the responses & incorporating the suggestions- I probably will be for some time, actually.
Several people pointed out that there was no validation of username/password so I wanted to mention that my first page & script http://busybody.w1.com/html/2010again.html check the validity of the username/password. I created 3 text files (one for each class) that have a list of user id & password which the script compares to what the user entered.
If anyone is curious, the Perl code for the first page is located at http://busybody.w1.com/html/2010b.pl.txt Wow, that is very cool- I just realized if I put braces around the URL it turns into a link. It's the little things that make me happy, I suppose. :)
As many of you pointed out, there are many other security loopholes that I will need to fix as I learn. Right now, since I am brand new to Perl, I am just trying to get the code working as best I can & am planning on tweaking it/learning about security issues once I get the basics going. I welcome all comments though as to how best do this as that is how I'll learn.
The second page allows them to upload their assignment & the third page shows them the history of what they've uploaded. The third page has not been created yet.
I just switched both my scripts over to using the CGI.pm module. (I can hear the cgi.pm fans cheering!!)
Question #1) Because cgi.pm has the built-in "text/html" header function, I need to find a different way to send the user to the next page. Previously I used: print "Location: /html/2010assignment.html\n\n"; but since CGI.pm has the built in function, it prints that to the screen which is obviously not what I want to do. Rather I want to send the user to the next HTML page.
The perl script that operates behind the second page has also been converted over to cgi.pm (code is included below) I was also able to get the variables working in the path $filename = "$HOME/classes/$CSC/$studentid/$assignment/outputfile.txt";
The problem was the variable ($CSC) was returning a value of uppercase CSC2010 while the folder was named lowercase csc2010. Once I renamed the folder to uppercase CSC2010, it worked. I also changed permissions on the folder to include "write" access.(Tadman- thank you for suggestion that I use "join". Once I get things working more smoothly, I will look into that. I agree- I do need to learn some about regular expressions as well)
Question #2: I am trying to print the variable $file which is holding the user input from the upload file box -->
Type path of document to upload or click "Browse" to navigate to docum
+ent:
<INPUT TYPE="file" name="path">
For some reason, my other variables will print with no problem with the exception of this one. I thought the variable would contain the path to the document the student wants to upload but, regardless of what it holds, it is stubbornly not anything printing out. Does anyone have any idea why?
While I realize I made a lot of progress today in my quest to learn some Perl, I'm left with the feeling that I have many more questions than when I began. (Sigh...)
Thanks again, everyone.
Laura
lfindle@mindspring.com
#!/usr/local/bin/perl -w
# 2010c.pl
use CGI;
require ("cgi-lib.pl");
my $q = CGI->new();
print $q->header();
$studentid = $q->param('studentid');
$CSC = $q->param('CSC');
chomp($CSC);
$assignment = $q->param('assignment');
$file = $q->param('file');
$HOME = (getpwnam("wfp51916"))[7];
$filename = "$HOME/classes/$CSC/$studentid/$assignment/outputfile.txt"
+;
open( OUTFILE, ">>$filename" ) or die ( "no go miss ");
print OUTFILE "$studentid\n";
close( OUTFILE );
print "$studentid<br>";
print "$CSC<br>";
print "$assignment<br>";
#below is the variable I cannot get to print
print "$file<br>";
| [reply] [d/l] [select] |
|
lfindle, Welcome to Perlmonks and Perl, and we all sympathize with your feeling:
While I realize I made a lot of progress today in
my quest to learn some Perl, I'm left with the
feeling that I have many more questions than when I began.
They often say in science that asking one question
finding the answer and finally knowing does not
salve one's need for understanding, but raises
one's need to know more.
Or as better said by Matt Cartmill, the anthropologist:
"As an adolescent I aspired to lasting fame, I craved factual certainty, and I thirsted for a meaningful vision of human life - so I became a scientist. This is like becoming an archbishop so you can meet girls."
Cheers, and I hope you enjoy your time here! :)
| [reply] |
Re: Using Variables in Path Names
by graq (Curate) on Nov 28, 2001 at 19:47 UTC
|
Lots and lots of very pertinent replies on the security problems relating to the task you have mentioned.
The only thing I haven't seen mentioned, which it looks like you need, is to check that the directory exists. Presumably your code is not just supposed to work for Laura, so unless you are anticipating all the required directory and subdirectory names, you will need to create them on the fly.
Small snippet:
print "Directory $dirname does".((-d $dirname)?" ":" not ")."exist.\n";
<a href="http://www.graq.co.uk">Graq</a> | [reply] |
Re: Using Variables in Path Names
by spartan (Pilgrim) on Nov 28, 2001 at 20:35 UTC
|
Although Laura indicates in her post that static names allow here to create the files, and others have opinted out that she may need to make sure that the directory structure is there, I thought I'd put in my 2 cents: Do not forget permissions, I see this alot. I can only speak of the pitfalls that are common on UNIX systems though, I do not know how IIS on NT/Win2k deals with permission problems. So to that end, check to make sure the permissions on the set of directories you will be writing to are indeed writable by the web server process. If the web server runs as NOBODY (a common user on UNIX boxes with little or no system wide permissions) make sure your directory structure is either owned by NOBODY, or depending what default group the user NOBODY belongs to, make the directory structure group writable by the appropriate group. I would not suggest world writable directories, they too can be a source of security problems. Good Luck, let us know how it turns out. -spartan
Very funny Scotty... Now PLEASE beam down my PANTS! | [reply] |