There's a lot that's missing from your question, so I'll have to make some guesses. And, since Stackoverflow is mostly about other people having similar problems reading these answers, some of this may not apply to you. Futhermore, most of this is about web security and not particular to Perl. You'd want to go through the same things in any language.
First, you say "anything works here". Don't let that be true. Consider that ..
, the virtual parent directory, specifies movement around the directory structure:
/home/../../memo/../../../target.pl
You end up with a file that you didn't want to expose. Not only that, if they were, through other means, able to make a memo symlink in the right spots, they are able to use that to move around too. That is, you can't really tell what file you'll get just by looking at the path because symlink (or hard links, too, I guess) can completely change things. What if memo was a symlink to /
?
Second, don't ever let remote CGI users tell you where a file is. That's just too much for them to decide for you. Instead, it looks like there are two things that you will allow them to supply. A directory in the second position and something at the end. Make them specify those two things in isolation:
https://localhost:8080/cgi-bin/memo.cgi?user=megaboz&thing=NewCEO
You still have to validate these two things, but it's much easier to do them separately than in the middle of a bunch of other things. And, since you are taking input from the user and mapping it onto the file system, you should use taint checking (perlsec), which helps you catch user input being used outside your program. To untaint a value, use a match and capture what you will allow. I suggest that you do not try to salvage any bad data here. If it doesn't match what you expect, return an error. Also, it's better to specify what you allow rather than come up with everything you will disallow:
#!perl -T
my( $user ) = however_you_get_CGI_params( 'user' ) =~ m/\A([a-z0-9]+)\z/i;
my( $thing ) = however_you_get_CGI_params( 'thing' ) =~ m/\A([a-z0-9]+)\z/i;
unless( defined $user and defined $thing ) { ... return some error ... }
Now, this doesn't mean that the values you now have in $user
and $thing
are true. They are merely valid values. Map those to whatever you need to fetch. Since you've constructed a path, checking that the path exists might be enough:
use File::Spec::Functions;
my $path = catfile( '/home', $user, 'memo', $thing );
unless( -e $path ) { ... return some error ... }