On 2026-05-08 06:53, Nutchanon Wetchasit wrote:
On Thu, 7 May 2026, Stormy-SDLU wrote:

What is best practice to disallow all access to the cgi-bin
except for the local web form.
I have a vague memory of 127.0.0.1 being usable by apache 2.4.x

Sanity check first: by "local web form", did you mean:

A. HTML form located on the same website.
Yes,
B. Browser accessing such form is running right on the server;
    but the attackers came from the Internet. (Unlikely)
No, intranet and from external I{P addresses
C. Browser accessing such form is running on same Intranet as the server
    (Intranet, not Internet); but the attackers came from the Internet.
As above, tested from  internal and external IPs
[snip]
F. A, B, and C combined.
See above
If what you meant was just A., then `.htaccess` alone would not help you
that much other than papering up a bit over the issue. [1]
Attacker could bypass such measure in a relatively-easy way
once he wised up to what you were trying to do.
Agreed.  At this point it's only about 75 effective

A more proper way to prevent such kind of abuse, is to implement
the same measure that's used for preventing cross-site request forgery (CSRF).
This is not really doable in `.htaccess`, and require you to switch the form
from static HTML to CGI, as well as modifying the CGI application itself
to add such protection. [2][4]

I'm looking at CSRF and other possibilities (thanks for the extensive notes below), but am wondering if I could use a modified
    <Directory /www/mysite/cgi-bin???>
        <LimitExcept GET HEAD>
            Require all denied
        </LimitExcept>
    </Directory>
for just the cgi directory?
It's in production, so I haven't tried yet

Again tnx and br -- Paul


But if your answer was not A. however, then we could continue to discuss
about using `.htaccess` to ward against such access.

Regards,
Nutchanon Wetchasit
(Just another Apache user)


[1] By checking the `Referer:` header value on the requests to the affected
     CGI application, against the expected URL of HTML form that was supposed
     to be invoking it. If it matches, let the request through;
     otherwise deny the request.

     The most straightforward way to do it would be via mod_rewrite's
     `RewriteCond`/`RewriteRule` directives:

         RewriteCond %{HTTP_REFERER} !=http://example.com/preciousform/
         RewriteRule cgi-bin/precious/program.cgi - [R=403,L]

     Other ways (with more side effect) are location-confined version
     of new-style mod_authz_core's `Require`:

         <Files cgi-bin/precious/program.cgi>
             SetEnvIf Referer "^http://example\.com/preciousform/$"; 
preciousform_referrer
             Require env preciousform_referrer
         </Files>

     And location-confined version of classic HTTPd 2.2-style
     mod_access_compat's `Allow from`/`Deny from`:

         <Files cgi-bin/precious/program.cgi>
             Order Allow,Deny
             SetEnvIf Referer "^http://example\.com/preciousform/$"; 
preciousform_referrer
             Allow from env=preciousform_referrer
         </Files>

     Note that:

     - These assume you're doing it in `.htaccess` at the root directory
       of the virtual host (or inside `<VirtualHost>` configuration itself).
     - The placeholder HTML form URL used my example is
       "http://example.com/preciousform/"; (beware that some place
       in the examples required a regular expression from).
     - The placeholder on-site location of the CGI application
       in my example being "cgi-bin/precious/program.cgi"
       (note the absence of slash prefix).
     - The "preciousform_referrer" part is a variable that would become exist
       when the request satisfies condition (having a specified
       HTTP Referer value). You may change the variable name, but make sure
       to change all of them in the example code to use the same changed name.
       But if you have more than one CGI programs which you're adding
       protection to in such way, make sure that code for each of them
       use different variable name from another CGI program.
     - Don't forget to substitute the placeholders I mentioned
       if you decide to use these.

[2] Usual method is when user accessed the form, in the form CGI script,
     you'd assign user a session cookie; then generate one random number,
     associate it with that session identifier, save that association
     on some kind of server-side database [3], and display the usual
     HTML form page, with the usual form, but with one additional hidden field
     (`<input type="hidden" ...>`) that has the value of aforementioned
     random number.

     And once user pressed submit on the form, and POST request arrived
     at the application's CGI script, check the session cookie,
     look up in the server-side database to find the correct random number,
     then check that it must match against the hidden field submitted
     from client. If any step failed, fail the request, say "Form expired"
     and direct user back to the original CGI form CGI.

     (Thus, when attacker blindly flung unsolicited POST requests
     to your CGI application, he would have neither valid session cookie,
     nor correct random number that your CGI application recognize;
     and your application would reject such requests)

     But this method is quite involved, and you also need to have both
     your CGI form and CGI application automatically weed out the
     "used"/"expired" random number associations in the database too.

     So in Perl CGI, I will not recommend this "usual" method;
     I would recommend TOTP method instead. [4]

[3] PHP for example, uses flat-file database for this kind of session data;
     and it has built-in ways of automatic data removal of expired sessions.

[4] The TOTP method of (limited) CSRF protection works by having
     a hidden field in the CGI form which contains an OTP number generated
     by using on-server TOTP key file, against the timeframe-number
     (Unix timestamp divided by 30) of the request for the form.

     Then the CGI application that services the POST request, would generate
     multiple OTP numbers, using the same on-server TOTP key file--
     against timeframe-number of the POST request's access time,
     as well as multiple timeframe-numbers prior to it
     for the period you deem the form to still be valid.
     (Generate 20 OTP numbers backward to compare,
     if you would like the form to stay valid for 10 minutes, for example)

     Then the CGI application would have to check that the hidden
     field was submitted in the POST request, and then try to match it
     to the OTP numbers it generated just now. If the submitted hidden value
     matched just one of these OTP number, the request was valid and
     you could proceed. Otherwise, reject it, say "Form expired" or whatever,
     then direct user back to the original CGI form CGI.

     (Thus, when attacker blindly flung unsolicited POST requests
     to your CGI application, he would not have the correct
     cryptographically-generated OTP number to provide;
     meaning your application could reject such requests)

     This method needs neither cookie nor server-side database;
     only a RFC 6238 TOTP library (which CPAN has several, I believe),
     and a key file-- which you guard with the same consideration
     as password file, but less critical.

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to