perlpathname

Validating a path in Perl


I am working on a lab for a class I am taking and I have a question about checking Perl strings for specific input.

Essentially what I want to be able to do is make sure that the input I am receiving from a user is something like:

/home/[anything is valid]/memo

The whole point of the lab is to prevent a pathname attack in a simple program that the instructor provided us with. So I want to check to make sure that the pathname provided by the user is within this format before doing anything with it.

I am currently using the abs_path() method in Perl to make get the absolute path of the string being passed in, but now I need to make sure that the absolute path contains what I have above.

Here is what I am trying to achieve:

my $input = "localhost:8080/cgi-bin/memo.cgi?memo=/home/megaboz/memo/new_CEO";
my $memo = '/home/megaboz/memo/new_CEO';
my $pathName = abs_path($memo);
if($pathName ne "/home/[anything works here]/memo/[anything works here]") {
       #throw an error
}
else {
       #process input
}

Any pointers?


Solution

  • Welcome to the wonderful world of regular expressions, which is something Perl is quite good with.

    Let's walk through how to construct one of these. First, we usually use forward slashes to denote a regex, i.e.

    /some-expression/
    

    but since your paths have forward slashes in them, doing so would involve a messy bit of string escaping, so we'll use an alternate delimiter with m instead.

    m(some-expression)
    

    Now, we want to start with /home/ and end with /memo. You can read all about the different syntax in the link above, but in regular expressions we use ^ and $ (called anchors) to represent, respectively, the start and end of the string. So our regex is going to look like

    m(^/home/SOMETHING/memo$)
    

    Now for the piece in the middle. We want anything to pass. Your general purpose "anything" regex is a dot ., which matches any single character. And we can apply the Kleene star *, which says "zero or more of whatever comes before". So .* together says "zero or more of anything at all".

    m(^/home/.*/memo$)
    

    There's our regex. To apply it, we use =~ to ask "does it match", or !~ to ask "does it fail". The way your code is structured, we want to check for failure.

    if ($pathName !~ m(^/home/.*/memo$)) {
        ...
    } else {
        ...
    }
    

    Regular expressions are fairly ubiquitous and can be used in basically any programming language, so it's definitely a skill worth having (although Perl is particularly well-known for having strong regex support, so you're in the right tool for string matching capabilities).