Forum Moderators: coopster & phranque

Message Too Old, No Replies

How do I do basic CGI file security?

         

david752

9:43 pm on Jun 23, 2002 (gmt 0)

10+ Year Member



I would like to write CGI files that can be called from server-side (PHP) code in my site's Web pages, but that cannot be browsed to by the remote user's browser or by Web pages the remote user might write.

The problem I'm trying to solve is illustrated by this example:

Let's say I want to write '/cgi-bin/counter.pl', a hit counter. It will access data file 'count.dat' also located in the cgi-bin dir. I want to increment the hit count by calling counter.pl from PHP (server-side) code in my home page (possibly sending GET or POST arguments to counter.pl), but I don't want the remote user to be able to call counter.pl and mess up the count.

That is, I want counter.pl to be callable from my Web site but not from anyone else's Web site.

Normally, of course, the user can browse to any CGI file. This will execute or interpret the file and send its output back to the user's browser. I would like this default behavior to continue for all CGI files except counter.pl.

I'm sure I'm missing something very basic indeed. It would be wonderful if someone can enlighten me, or point me to a good tutorial.

Thanks,
David

mdharrold

1:17 am on Jun 24, 2002 (gmt 0)

10+ Year Member



Assuming I understand you correctly, you could use:
if ($ENV{REQUEST_URI} ne "/cgi-bin/counter.pl")
{execute code;}
else
{error message or redirect because they are calling it directly;}

david752

10:26 pm on Jun 26, 2002 (gmt 0)

10+ Year Member



I've done some experiments:

I'm using Apache/Unix. I'm using a "printenv" Perl in my cgi-bin dir. This cgi program shows the value of %ENV for each key.

I'm doing three tests with this printenv.pl:

1. Calling it directly as a browser URL.
2. Calling it from a page on the same site in javascript as "location.href = '...';".
3. Calling it from a page on the same site using the server-side preprocessing language PHP as "readfile(...);".

The result is that REQUEST_URI is always the same (/cgi-bin/printenv.pl).

David

mdharrold

1:20 am on Jun 27, 2002 (gmt 0)

10+ Year Member



I did misunderstand.
Why not have your PHP send a query string that makes the script work?

#!/usr/local/bin/perl

print "Content-type: text/html\n\n";
if ($ENV{QUERY_STRING} eq "oktorun")
{execute code;}
else
{print "error message";}
exit(0);

Then just change your PHP call to

counter.pl?oktorun

Anyone browsing your cgi-bin would then have to call the script with the correct query string or else it doesn't work.

Key_Master

1:48 am on Jun 27, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



Since your running your SSI scripts from static Web pages you can use PHP's equivalent of Perl's $ENV{'DOCUMENT_URI'} for better security. The following script tibit will check to make sure the script request originated from a Web page within your site. This is the Perl version of the code (sorry, I don't code in PHP) so somebody will have to translate it into PHP for you. Place it near the top of the script where it will execute first.

unless ($ENV{'DOCUMENT_URI'}) {
print "Content-type: text/html\n\n";
print "Unauthorized Access!\n";
exit;
}

amoore

3:30 am on Jun 27, 2002 (gmt 0)

10+ Year Member



Let me get this straight: You want a script that a process on your machine can call, but other people can't? Don't make it a CGI script. Just write a script and stick it in /usr/local/bin or somewhere. Call it from your PHP (or whatever) script.

Does that cut it?

If you must have it be a CGI script, use .htaccess to deny requests from IP addresses other than your own. That way, only requests originating on your machine can hit it.

david752

6:22 pm on Jun 27, 2002 (gmt 0)

10+ Year Member



I guess I didn't make it clear enough in my question that what I really want to do is to hide my data file(s) from users but not from the Web pages that use them.

mdharrold: I like your idea of using an ordinary URI with the args set to a password. It is simple, I can change the password if I need to. Of course, it still requires a CGI program so the data files in cgi-bin can be accessed.

Key_Master: Your idea sounds interesting, but DOCUMENT_URI is not listed in the help file for Perl, nor does it appear in the output generated by printenv.pl. I don't see any useful variables in PHP, either (except for HTTP_REFERER, but it usually is not set at all).

amoore: Your solution, to put the data files in a dir protected by an .htaccess file, sounds like the best one to me, since it depends completely on the server and it protects the data itself, not programs or pages that use the data.

Now I have to figure out what to put in the .htaccess. I'll look in the Apache doc. The old trial and error method :).

And I'll have to choose between the password and the .htaccess method. It is not obvious to me which is better for general use.

Thanks, all, for your excellent help.

David

Key_Master

10:31 pm on Jun 27, 2002 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member



>>>Your idea sounds interesting, but DOCUMENT_URI is not listed in the help file for Perl, nor does it appear in the output generated by printenv.pl.

DOCUMENT_URI is a SSI environment variable and will not return anything if you call any script that uses it from the address bar (hence it's effectiveness). It only returns the virtual path/filename of the Web page the script was called from. Unlike REQUEST_URI or HTTP_REFERER it can't be spoofed. I can stickymail you an example if you like.

Overview here [ku.edu] of different CGI/SSI environmental variables.

amoore

11:00 pm on Jun 27, 2002 (gmt 0)

10+ Year Member



what I really want to do is to hide my data file(s) from users but not from the Web pages that use them.

Don't put them in the document root, then. Apache won't serve them up that way, but your scripts can open them and read and write to them and such.

david752

9:35 pm on Jun 30, 2002 (gmt 0)

10+ Year Member



After thinking and experimenting, I believe that amoore's first suggestion is an ideal solution.

Here are the details, in case anyone else whose server uses Apache is interested.

I put all my data files in a new directory (rather unimaginatively called "data") and added an .htaccess file (my server runs Apache/Unix, my development server runs Apache/Windows) with the following content:

<Files ~ ".*">
Order allow,deny
Allow from 127.0.0.1 <--- Replace with your IP
Satisfy All
</Files>

This tells the server to disallow any references to files in "data" except those that directly come from the server. That means that the files can only be accessed in server-side code (CGI files, ASP, PHP, etc.).

Thanks for your help.

David