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

Hello Monks,

I use flat text database Perl script. It search all or part of online database and display the results in the html template(table based). There is one problem: how to set the script not to display all the database when you just type in the URL? I mean to prevent database from listing/viewing.
When the user type in browser http://webhost/cgi-bin/csvsearch.pl or http://webhost/cgi-bin/database.txt, it get all database at once, all database drops out at once! Its not look very nice.

#!/usr/bin/perl ########################################################## my $CSV_file = "database.txt"; my $HTML_template = "template.htm"; my $no_matches_found = "Sorry, no results found"; my $ID_use = 0; my $ID_field_name = "ID"; ########################################################## #%FORM = parse_cgi(); print "Content-type: text/html\n\n"; (my $head, my $tmp, my $foot) = get_html($HTML_template); $qs=$ENV{'QUERY_STRING'}; ##read db my @data = read_file($CSV_file); chomp $data[0]; my @fields= split('\|', shift @data); $base_length = @data; error("You have bad file!") if !@fields; error("Database is clear!") if $base_length<1; if($qs =~m/header=([^\&\Z]*)/){push @header,$1;} if($ID_use && $qs =~m/show=([^\&\Z]*)/){ @data = search($1,$ID_field_name); } else{ @conditions=split(/&/,$qs); my $a=0; foreach (@conditions){ ($name, $value) = split(/=/, $_); if($name eq 'search'){ $FORM{search} = $value; @data = search($value, $header[0]); } elsif($name eq 'header'){} elsif($_=~/([^=<>!]+)!=([^=<>!]+)/){@data = search($value, $1, + "!=");} elsif($_=~/([^=<>!]+)=([^=<>!]+)/){@data = search($value, $1); +} $a++; } } my $result; ##matched data foreach(@data){ chomp; @line = split('\|', $_); $a=0; %INSERT=(); foreach(@fields){$INSERT{$_} = $line[$a++];} $result.=get_record($tmp) } %INSERT=(); $INSERT{'#_matches'} = @data; $INSERT{'#_total'} = $base_length; $result = $no_matches_found."<br>" unless @data; print get_record($head), $result, get_record($foot); undef $result; undef $head; undef $foot; exit; ######################################################### sub search{ my $word=shift; my $field=shift; my $action=shift; $word=~tr/+/ /; $word=~s/%([\dA-Fa-f][\dA-Fa-f])/pack ("C",hex($1))/eg; my $position=-1; my $a=0; if($field){ $field=~tr/+/ /; $field=~s/%([\dA-Fa-f][\dA-Fa-f])/pack ("C",hex($1))/eg; foreach(@fields){$position=$a if $_ eq $field; $a++;} } my %match; $word =~s/ +/ /g; my @new_data=(); my @keys= split(" ", $word); if($action eq '!='){for(0..@data-1){$match{$_} = 1;}} foreach $key (@keys){ $a=0; foreach $record (@data){ @line = split('\|', $record); if($field && $position>-1){ $match{$a} = ($line[$position] =~ m/\b\Q$key\E\b/i +) ? 1 : 0; $match{$a} = !$match{$a} if $actio +n eq '!='; } else{ foreach(@line){if ($_=~m/\Q$key/i){$match{$a} = 1; las +t;}} } $a++; } } $a=0; my $b=0; foreach(@data){ $new_data[$b++] = $_ if $match{$a}; $a++; } return @new_data; } sub get_record{ my $text = $_[0]; $text =~ s{<<(.*?)>>}{exists($INSERT{$1}) ? $INSERT{$1} : ""}gsex; return $text; } sub get_html{ my @txt = read_file($_[0]); my $txt; foreach(@txt){$txt.=$_;} $txt=~/(.*)<template>(.*)<\/template>(.*)/s; error("Template-tag not found!") if !$1 or !$2; return ($1,$2,$3); } sub read_file{ open(F, $_[0]) || error("Can't open file $_[0]!"); my @data = <F>; close F; return @data; } sub error{ print "<html><head><title>Error</title>$style</head><body><br><br> +<br><font color=red><h3>$_[0]</h3></font></body></html>"; exit; } ##########################################################

2004-12-03 Janitored by Arunbear - added readmore tags, as per Monastery guidelines

Replies are listed 'Best First'.
Re: How to prevent database from listing/viewing?
by lithron (Chaplain) on Dec 03, 2004 at 14:18 UTC
    First off, use the wonderful CGI module that comes with perl. Then read up on your webserver documentation so you can keep people from reading files inside the /cgi-bin/ directory. I believe Apache comes pre-configured to not let files be read from the cgi-bin directory..
      Unfortunately webserver I use provude very poor techical support.
Re: How to prevent database from listing/viewing?
by trammell (Priest) on Dec 03, 2004 at 15:10 UTC
    One solution is to put the data "outside" the directories that the webserver displays, e.g. if your script is in /var/www/cgi-bin/, put the data in /var/lib/myapp/.
      One of the possible way is add one or two lines of code, that will handle "empty requests"(running script with no requests), for example, when running the script with no requests set 0 to display all the database or 1 to display the $no_matches_found message. my $default_show_mode = 0;

      But dont know how to implement this, as well what do with database.txt listing.
      If I put data outside the cgi-bin, I will need rewrite scipt, since it will looks data in'standard' place(i dont know how do this). What I have is index.html page placed in cgi-bin folder, it prevents from direct access to cgi-bin directory, but can no prevent from mentioned listing.
        Maybe I'm misunderstanding, but I'd expect the only line you'd have to change is:
        my $CSV_file = "/the/path/to/the/database.txt";
Re: How to prevent database from listing/viewing?
by Miguel (Friar) on Dec 03, 2004 at 17:50 UTC
    in extreme, do something like this:

    chmod 600 database.txt (owner can read and write, the others can do nothing)

    if you try to read http://www.yoursite/cgi-bin/database.txt
    you will get a Forbidden alert and a log in your logs file

      Yes, this help: setting the 600 CHMOD to databse.txt O.K.
      So one problem remains only: prevent from running the script with no requests: browser http://webhost/cgi-bin/csvsearch.pl
        Does someone have some ideas?
Re: How to prevent database from listing/viewing?
by elwarren (Priest) on Dec 03, 2004 at 15:59 UTC
    This is a file permissions issue with your webserver, really not much you could do with the code. You will need to either move to file to another directory and update the script to load this new file location. You could restrict privs on the file so that only the webserver process can read the file, but all your web requests will come in with this same user anyway. If you don't have any control over your server(which is how it sounds) then this won't work either.

    The only change required to move this file would be updating this line to point to a different dir:
    my $CSV_file = "database.txt"; like this my $CSV_file = "hidden/dir/database.txt";
      Yes, this is webserver issue. I undersnad now, just change this path. Or another way, may be, rename database to something.txt (it's hidden in the script no nobody can find it)or change extension, like database.pl
      Will this work also?
        I hadn't thought about renaming the file, but that could work. My webserver is configured to not serve files that are named beginning with a dot. This is to prevent the .htaccess .htpasswd .listing maintenence type files from being served to the world. Again, this all depends on your server setup, but you could try renaming the file to .database or something.

        HTH
Re: How to prevent database from listing/viewing?
by Jasper (Chaplain) on Dec 03, 2004 at 15:58 UTC
    Change the line:$qs=$ENV{'QUERY_STRING'}; to $qs=$ENV{'QUERY_STRING'} || 'string that won\'t be found because it contains the word "fitbin", and that won\t be in there';