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

Howdy,

I recently figured I should try to see how secure my CGI stuff was, and... well, it isn't. I usually taint, but not very well. I don't shell user input, but I don't check input if it is only for DB insertion.

I'm trying to learn how/what to fix in all this. I came across one problem I don't know how to solve. If I use a CGI upload, I get a file-handle passed back to me. How do I verify that this file-handle is valid and represents a text-file? Further, what's to stop somebody from passing me an enormous file that crashes my server?

There must be obvious answers to this problem, but I'm wading through super-search results on CGI security and haven't come across it yet....

Replies are listed 'Best First'.
Re: CGI File Upload Security
by tcf22 (Priest) on Sep 15, 2003 at 18:50 UTC
    You could use
    use CGI qw(-private_tempfiles);
    As for the huge file, you could limit the size the a http POST can be, since file uploads have to be posts(as far as I know anyway.)
    The CGI docs on CPAN, have a little more on these things.

    UPDATE: You could always change the following line in CGI.pm.
    # Set this to a positive value to limit the size of a POSTing # to a certain number of bytes: $POST_MAX = -1;
    or set $CGI::POST_MAX equal to a positive value in your script.

    - Tom

Re: CGI File Upload Security
by sulfericacid (Deacon) on Sep 15, 2003 at 19:48 UTC
    I've been working with file uploads via CGI for a while now. To protect against large files, you /can/ limit the file size that the user is allowed to upload. I think you'd probably use $CGI::POST_MAX=1024 * 100; # max 100K at the top of your script. That way no file over 100k will be uploaded and if it's tried, it'll throw an error at them.

    As for making sure it's a text file. My method doesn't test securely but it does check to make sure the filename is whatever you want. (doesn't mean they can't upload an exe and call it log.txt, mind you). Here's the snippet I use in all my upload scripts (change it to fit your needs):

    my $type = uploadInfo($remotefile)->{'Content-Type'}; unless ( $type eq 'image/pjpeg' || $type eq 'image/gif' || $type e +q 'image/bmp') { print "Wrong! This is not a supported file type."; exit; }
    I hope this helps,

    sulfericacid

    "Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

    sulfericacid

Re: CGI File Upload Security
by tomhukins (Curate) on Sep 15, 2003 at 19:30 UTC
    I don't check input if it is only for DB insertion

    If you use placeholders in your SQL queries, you don't need to worry about escaping issues, and you can write cleaner database queries that don't contain Perl code or variables.

Re: CGI File Upload Security
by tachyon (Chancellor) on Sep 16, 2003 at 06:33 UTC

    There are modules that will determine file type for you, however if you just want to check for a text file you can probably just use this heuristic:

    my $file = 'C:/tmp.zip'; open F, $file or die $!; read( F, my $buf, 1024 ); close F; print is_text($buf) ? "$file is TEXT\n" : "$file is BIN\n"; # alternatively you can just... print -T $file ? "-T $file is TEXT\n" : "-T $file is BIN\n"; sub is_text { my ($data) = @_; return 0 unless $data; return 0 if $data =~ m![\x00-\x08\x0b\x0e-\x1f]!; return 1; }

    All it does is look for any chars that should not be in a TEXT file but are found in binary files. The 1024 char is arbitrary but is the one used by file. There is also the -T file test in Perl which does something similar but needs a real file or handle rather than just a data buffer...

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: CGI File Upload Security
by jonadab (Parson) on Sep 16, 2003 at 04:07 UTC
    I don't shell user input, but I don't check input if it is only for DB insertion.

    That's fine, but you should consider data coming out of the database to be tainted. I believe there's an option that can be passed to connect when you open the db handle that will cause data coming from the db to be marked as tainted. Then if you try to do anything insecure with it, you'll get an error relevant to what you were trying to do, so you know what your checking needs to ensure.

    Further, what's to stop somebody from passing me an enormous file that crashes my server?

    Bandwidth. If they have enough bandwith to send you a file large enough to crash your server, there are a dozen other ways they can DOS you. Set a limit if you're worried about it (other posts in the thread tell you how), but I doubt it will be a real issue.


    $;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$ ;->();print$/
Re: CGI File Upload Security (with Data::FormValidator)
by markjugg (Curate) on Sep 17, 2003 at 02:09 UTC
    You may be interested in Data::FormValidator::Constraints::Upload. It can validate the upload based on the file format, the file size, and maximum image dimensions (if that applies). It uses File::MMagic for some of the brains of the file type checking. This means that rather than trusting the file name extension or the content type that was sent, it examines the contents of the file to see if it can figure out the type based on what it really is.

    I maintain that module, and feedback is welcome.

    Mark