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

Hopefully I don't come off as a total moron, but I am having some trouble... Please keep in mind I started working with Perl about 3 weeks ago :)

I am using a standard FILE input to handle image uploads to a site. I am throwing the value into a string and scrubbing the local path (c:\documents and settings\blah blah blah\image.jpg) to end up with just the file name.

The expression I am using seems to work perfectly when I use firefox, but in IE 7 it still seems to be passing the full local path. Here is the expression and method I am using:

$file = ~m/^.*(\\|\/)(.*)/; open(LOCAL, ">../../htdocs/images/photos/$file") or die $!; while(<$file>) { print LOCAL $_; }
I would think that since this is all handled by Perl that the browser wouldn't make a difference, but it definitely seems to be the case. I am thinking maybe the value that IE sends in POST is different than the value that Firefox is sending...but I just can't figure it out.

Any idea why this works in firefox/chrome/safari/opera but not IE?

Thanks a ton, everyone.

Replies are listed 'Best First'.
Re: Scrubbing a local path in a file upload
by igelkott (Priest) on Feb 27, 2009 at 23:07 UTC
Re: Scrubbing a local path in a file upload
by hbm (Hermit) on Feb 27, 2009 at 23:50 UTC

    I assume it's a typo, but you have a space betwixt =~, which assigns a large number to $file! It's kinda cool, actually; it evaluates to ~0, or 2**32-1 on my systems, probably yours too. (Unless you have use integer;.)

    Typo aside, whenever you have slashes in a RE, it's best on the eyes to choose a different delimiter.

    And instead of your first set of parens, you probably want (?:\\|/) or [\/], neither of which stores the match.

    And, you were just matching (with 'm'), not substituting.

    With all that, you could do the following, which strips everything up to and including the last slash

    $file =~ s#^.*[\/]##

    (Or use File::Basename per the other suggestion.)

    Update: Slight revisions.

Re: Scrubbing a local path in a file upload
by ig (Vicar) on Feb 28, 2009 at 00:05 UTC

    I don't know why it works with some browsers and not others. I am surprised it works at all as you are using an assignment to $file (=) rather than the binding operator (=~).

    You might try the following:

    $file =~ m/^.*(\\|\/)(.*)/;
    open(LOCAL, ">../../htdocs/images/photos/$2") or die "$file: $2 - $!";
    while(<$file>) {
    print LOCAL $_;
    }
    

    If this doesn't work, you will have a copy of the original string and what was captured as the filename in your error log. These should help you understand what is happening.

Re: Scrubbing a local path in a file upload
by locked_user sundialsvc4 (Abbot) on Feb 28, 2009 at 03:27 UTC

    You should never have to worry about being ridiculed as a n00bie here. We don't do that. Welcome.

Re: Scrubbing a local path in a file upload
by bichonfrise74 (Vicar) on Feb 28, 2009 at 00:58 UTC
    Is this what you are looking for?
    #!/usr/bin/perl use strict; my $file_name = "testme"; my $full_file = "../../htdocs/images/photos/$file_name"; my $parse_name = ( split( "/", $full_file) )[-1]; print $parse_name;
Re: Scrubbing a local path in a file upload
by Anonymous Monk on Feb 28, 2009 at 08:35 UTC
    You should chdir or use absolute paths. You should also limit what can appear in a filename, or better yet, generate your own filenames. You should also use -T taint
Re: Scrubbing a local path in a file upload
by Anonymous Monk on Feb 28, 2009 at 19:34 UTC

    You allow a possible malicious remote user to create and overwrite arbitary files on your webserver. I don't think that this is a pretty good idea. In fact, your webserver could easily be abused to serve malware.

    Use a second input field to allow the user to specify an image name. Validate that input, restrict it to a set of safe characters. Abort the request if the name value is not valid. (E.g. use CGI::Carp and $name=~/^[A-Za-z0-9-_]$/ or die "Invalid name"

    Make sure only images can be uploaded. Use one of the CPAN modules to validate the upload, don't rely on ANYTHING sent by the user, neither MIME type nor image file name. For example, Image::Size returns an error if the upload is not an image. This is also the place to restrict the image file size and the image dimensions. You don't want your users to upload gigabyte sized pr0n astronomical images, do you?

    Abusing the filename on the client machine as filename on the server is also very inconvienient. What if I want to upload img0093.jpg as earth_and_moon_as_seen_from_the_surface_of_jupiter.jpeg? I would have to rename or copy the file locally(!) before uploading it, then I would have to rename the file back or delete the working copy. You could use some simple logic (either in Javascript on the client or in Perl on the server or both) to use the client's filename if no image name was entered.

    Note that all these tests still don't solve the problem that any user can overwrite any file. If you don't want that, give each upload(!) a unique ID. UUIDs could help, or a column with an automatically generated ID in an RDBMS (use DBI). Use the ID to attach other attributes, like the image name, to the image. Use the ID as filename on the server, or store the image as BLOB in the RDBMS.

    Alexander

      Oops, missed one thing: The MS IE sends the ENTIRE local filename to the server, unlike ALL other browsers I know. Don't ask why, it's just plain stupid and insecure, just what you would expect from the MS IE if you have suffered from it long enough.

      Alexander

        Thanks to everyone for helping me with this! I got it solved within 5 minutes of reading this thread. You guys rock.
Re: Scrubbing a local path in a file upload
by Bloodnok (Vicar) on Feb 28, 2009 at 14:43 UTC
    I can't be certain (who can?:-), but I'd guess you've hit on browser specific functionality.

    BTW, you probably want to rewrite = ~m/^.*(\\|\/)(.*)/ as =~ m,^.*(:?\\|/)(.*), or even =~ m,^.*[\\/](.*) - thus incorporating observations mode elsewhere on this thread and also the fact that m/^.*(\\|\/)(.*)/ will return the path to the file as $1 and the file itself as $2.

    A user level that continues to overstate my experience :-))