Re: prevent perl script running from browser
by soonix (Chancellor) on Oct 01, 2017 at 04:16 UTC
|
This behaviour is intended, see e.g. here.
If it should not be executed upon browser request, then move it somewhere else, so that it can't be accessed that way. | [reply] |
|
|
Thank you. Is there some code I can put at the top of the script to check that it is being fun from the server and quit if it's not? I would rather not move it outside of cgi-bin and not sure that would solve the problem anyway unless it were moved outside the public_html directory.
| [reply] |
|
|
One possibility is that you could examine the HTTP_REFERRER.
When called from a web page, this is usually set.
If your "server" calls do not set this, or set it to a specific valye, you could use this as a detection mechanism.
Your question is a little strange, so I will not comment on the relative in-security of this method.
All power corrupts, but we need electricity.
| [reply] |
|
|
|
|
|
Re: prevent perl script running from browser
by afoken (Chancellor) on Oct 01, 2017 at 10:10 UTC
|
As others said: If you don't want the script to be started from the webserver, don't place it in directories accessible via the webserver.
In case of shared hosting, you are often limited to directories accessible via the web server. A .htaccess file can help. But you can also check for the environment variable GATEWAY_INTERFACE. Unless you have really messed up your setup, it is set if and only if a program is executed as CGI (i.e. from the webserver), it is set by the webserver, and it can't be changed by HTTP requests. See https://tools.ietf.org/html/rfc3875#section-4.1.4:
if (exists $ENV{'GATEWAY_INTERFACE'}) {
# invoked as CGI
# print out a short message and abort
my $q=CGI->new();
print
$q->header(-status=>'400 Bad Request', -type=>'text/html'),
$q->start_html(-title=>'Bad Request'),
$q->h1('Bad Request'),
$q->end_html();
exit 0;
}
# not invoked as CGI if you get here
And by the way: from which digital junkyard did you copy this nonsense?
$ENV{PATH} = "/usr/sbin/sendmail -t -i"; #for sendmail
$ENV{'PATH'} contains a ":"-separated list of directories to search for executables. Putting the name of an executable and parameters there is nonsense and won't do what you might expect. You want $ENV{'PATH'} set to a safe value, this is usually /bin:/usr/bin and nothing else.
Some more notes:
- my $brow = $ENV{'HTTP_USER_AGENT'}; - Consider the User-Agent HTTP header and the HTTP_USER_AGENT CGI environment variable to contain nothing but junk. The User-Agent identification is completely broken since the mid-1990s, and it is completely under user control. You CAN NOT RELIABLY DETECT the browser by guessing data from that header. Don't use it at all. Any code relying on that header is broken by design.
- my $ref = $ENV{'HTTP_REFERER'}; - Consider the Referer HTTP header and the HTTP_REFERER CGI environment variable to contain nothing but junk. The referrer information is completely under user control and may contain any nonsense or attack you can (and can not yet) think of. You CAN NOT RELIABLY DETECT the page the browser came from by guessing data from that header. Don't use it at all. Any code relying on that header is broken by design.
- You don't unset $ENV{'HTTP_PROXY'}. There is a known attack on CGIs that can redirect HTTP requests issued by (not to!) CGIs to a server under control of the attacker. Especially if you are running your code on a shared hosting server, don't rely on the hoster to fix that problem for you.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] [select] |
Re: prevent perl script running from browser
by haukex (Archbishop) on Oct 01, 2017 at 07:42 UTC
|
As others have commented, your question is a bit unusual. If you could explain the background of why you want to keep your script in the public cgi-bin but only allow the server to access it, in other words what bigger problem you're trying to solve, we might be able to suggest more and better solutions. Also, who will be accessing your script, and will it always be accessed via HTTP/CGI, or could it be run from the command line too? (The latter would give you the best way to make sure it only gets run locally.)
Anyway, limiting access to something on a web server is usually done via access controls in the web server, for example Apache has .htaccess files, e.g. the Allow directive (combined with Order and Deny).
Another possibility might be to check $ENV{REMOTE_ADDR} in your script, but then your script would be taking over the job that the web server normally does, and personally I'd try not to rely on the CGI environment variables too much.
| [reply] [d/l] [select] |
Re: prevent perl script running from browser
by LanX (Saint) on Oct 01, 2017 at 12:05 UTC
|
> How can I make it so it can only run from calls to it that originate on my server.
do you mean "called from a browser that runs on the same host"?
Smells like an xy problem for me, please try to be more explicit of how you want to call your script.
you might want to check the environment vars for the IP of the request.
( REMOTE_ADDRESS IIRC )
| [reply] |
Re: prevent perl script running from browser
by snowchild (Novice) on Oct 01, 2017 at 15:50 UTC
|
Hello everyone
You are all being very helpful and some have asked what the purpose of this script is. Digital junkyard is correct but it was not copied. Rather, back in 2000 it was custom written for us by a CGI programmer and we have been using it along with a more modern php version for a long time, hence the outdated codes.
Many of you will frown upon what I am about to tell you but please bear in mind that we have been running our shopping cart this way since Google was a corporal.
Here is what happens.
The site is all SSL using https, all pages are PHP. We sell niche configurable custom made products that we need to be able to check manually and often times have to correct with the customer to make them producible in the way they want them. It's complicated. If we use a gateway to process cards we will be constantly issuing credits or adjustments to the total price, so instead, we take all the details of the order and the customer payment info and encrypt it using GnuPg on the server. To accomplish this we use the perl script to interact with GnuPg by sending the details of the order to the script. After encryption, the script emails the result to us and we decrypt it on this end with the private key. We then store the decrypted information on a password protected machine that is not networked.
Lately, I have been seeing some direct access to the script from browsers. There are no parameters being passed to it and the only way for the script to run is from the forms that check for the presence of the parameters being not empty. I am therefore assuming that someone is trying to hack the script. I may be totally off base and yes the script is old but I am not a perl programmer. I can write simple PHP and am good at vanilla JS and jQuery, HTML5.
Here is the total script with sensitive information xxxxxxxxxx'd out. I would appreciate any help the monks can give with this junkyard dog.
#!/usr/bin/perl -T
use CGI::Carp qw(fatalsToBrowser);
use CGI qw/:standard/; # load cgi (functions for using forms a
+nd generating HTML)
$CGI::POST_MAX=10240; # limit data to 10k
$CGI::DISABLE_UPLOADS =1; # prevent file uploads with this script
+ :/
use Fcntl qw(:flock); # file lock used by the order counter
my $serv = $ENV{'SERVER_NAME'};
my $ip = $ENV{'REMOTE_ADDR'};
my $brow = $ENV{'HTTP_USER_AGENT'};
my $ref = $ENV{'HTTP_REFERER'};
$ENV{PATH} = "/usr/sbin/sendmail -t -i"; #for sendmail
my $text= param("order"); # The text of the order hopefully
my $email= param("email"); # The address for confirmation hopefull
+y
my $totals= param("totals"); # The order totals
# sends us an email with customers email in case an order somehow does
+ not get processed correctly
$to = 'xxxxxxxxxxxxxxxxxxxxxxx';
$from = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$subject = 'A customer has placed an order';
$message = 'Email of user= '.$email;
open(MAIL, "|/usr/sbin/sendmail -t");
# Email Header
print MAIL "To: $to\n";
print MAIL "From: $from\n";
print MAIL "Subject: $subject\n\n";
# Email Body
print MAIL $message;
close(MAIL);
#print "Content-type: text/html\n\n";
#print $text;
#exit;
# send to error page if no params detected.
if (!$text)
{
# there was an error we did'nt get any text
# do error stuff here
print redirect("/Order_error.php");
exit;
}
$text.= "\n\n---------------------------------------\n\n" .
"Server : $serv\n" .
"Browser : $brow\n" .
"IP : $ip\n" .
"Ref : $ref\n\n";
#print $text;
#exit;
# $gpg_exe program to send data to -- in this cas
+e gpg
# $gpg_opts commandline arguments for program -- in this cas
+e encrypt using xxxxxxxx key
# $output_file file name/path to recieve result
$gpg_path = "/usr/bin/gpg";
$gpg_options = "--homedir /home/xxxxxxxxxxx/.gnupg --no-permission-war
+ning --no-use-agent --batch --no-version --no-tty --always-trust --en
+crypt --textmode --armor --default-recipient xxxxxxxxxxxxxxxxxxxxxxxx
+";
$gpg_public_key_user_id = "xxxxxxxxxxxxxxxxx";
my $rnum= 1;
my $out_file = "/home/xxxxxxxxxxxxxx/public_html/cgi-bin/encPGP/".get
+_date() ;
while(-e "$out_file$rnum") {$rnum++;}
$out_file = "$out_file$rnum";
#print $out_file;
$gpg_command = "$gpg_path $gpg_options ";
$gpg_command .= "-r $gpg_public_key_user_id ";
$gpg_command .= ">$out_file";
open (gpgCOMMAND, "|$gpg_command");
print gpgCOMMAND $text;
close (gpgCOMMAND);
open(gpgOUTPUT, $output_file);
while (<gpgOUTPUT>) {
$gpg_output .= $_;
}
close (gpgOUTPUT);
unlink($output_file);
#return($gpg_output);
#1;
my $date = get_date();
open (SLS, "| /usr/sbin/sendmail -t -i");
print SLS "To: xxxxxxxxxxxxxxxxxxxxxxxxx\n";
print SLS "From: ".$email."<".$email.">\n";
print SLS "Subject: Online Order $date\n\n";
my $gpg_out="";
open(gpgOUTPUT, $out_file);
while(<gpgOUTPUT>) {
$gpg_out .= $_;
}
close (gpgOUTPUT);
print SLS $gpg_out;
close (SLS);
open (USR, "| /usr/sbin/sendmail -t -i");
print USR "To: ".$email."\n";
print USR 'From: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'."\
+n";
print USR "Subject: Online Order : $date\n\n";
print USR "Thank you for shopping at xxxxxxxxxxxxxxxxxxxxxxxx\n";
print USR "Your order has been received and is being processed.\n\
+n";
print USR "You will receive a confirmation email containing your o
+rder details within\n";
print USR "24 hours, weekends and holidays excepted.\n\n";
print USR "Order Dept.\n";
print USR "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n";
print USR "Toll Free xxxxxxxxxxxxxxxxxxx\n";
print USR 'Email: xxxxxxxxxxxxxxxxxxxxxxxx.com';
print USR "\n\n";
close(USR);
my $url="/Order_Successful.php?v=".$totals;
my $t=1; # time until redirect activates
print "Content-type: text/html\n\n";
print "<META HTTP-EQUIV=refresh CONTENT=\"$t;URL=$url\">\n";
sub get_date()
{
my($sec,$min,$hour,$mDay,$mon,$year,$wday,$yday,$isdst) = localtim
+e(time);
$year = $year + 1900;
my @month = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep"
+,"Oct","Nov","Dec");
my $date1 = "$mDay"."_$month[$mon]_$year"."_";
return $date1;
}
sub get_order_num()
{
# reads the number of orders from a file with locking and error
+checking
# and adds one to it
sysopen(FH, "/home/xxxxxxxxxxxxxxxxx/cgi-bin/encPGP/order_cnt.txt"
+, O_RDWR|O_CREAT) or return(-1);
flock(FH, LOCK_EX) or return(-1);
my $num = <FH> || 0;
seek(FH, 0, 0) or return(-1);
truncate(FH, 0) or return(-1);
(print FH $num+1, "\n") or return(-1);
close FH or return(-1);
return($num);
}
| [reply] [d/l] |
|
|
The first step would be to not have this script accessible as a CGI script at all. It should never be below /cgi-bin or wherever all PHP files of your site live.
NEVER accept untrusted user data in the headers of an email. The email parameter may well contain newlines which will turn your script into a spam cannon. Only ever include the user data in the mail body (if at all). If you feel that it is important to have the form submission appear as a mail from somebody, at least remove all whitespace from it.
As your mail script outputs some HTML, it seems that your intention still is that a browser accesses the script. Then you should rethink your knowledge about HTML and HTTP and how they interact.
The second step would be to replace all usage of sendmail with MIME::Lite to send the mail.
A potentially useful script is the FormMail script from nms-cgi. That one does not do the encryption of in-flight data, so you will have to add that yourself.
| [reply] [d/l] [select] |
|
|
That looks scary.
I've already explained some of the problems with the first few lines of the code. Let me add some more notes for the remaining part.
I would disable that script NOW. Just remove it from the server, then fix the problems. There are at least three obvious injection problems that need to be addressed, and there is a race condition in encrypting the form data.
Alexander
--
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
| [reply] [d/l] [select] |
|
|
hi Alexander
Thank you for a very concise run down on my garbage script.
I hope you realize that for most of the comments you wrote line by line I have no idea what you are talking about LOL.
As I said earlier, someone wrote this script 17 years ago and since then it has had some minor changes made by others from trying to make it work on various hosting servers. I think it would be best as you say, to remove the script ASAP and try to write a better solution with PHP.
In the meantime I am stuck with it. It does the job and no I have not noticed any errors in the output or mixing up of emails being sent, but as you say it's got more holes in it than swiss cheese. I cannot disable it until I have something better to replace it with. Such is life.
Thanks again
| [reply] |
|
|