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.
B. Browser accessing such form is running right on the server;
   but the attackers came from the Internet. (Unlikely)
C. Browser accessing such form is running on same Intranet as the server
   (Intranet, not Internet); but the attackers came from the Internet.
D. A and B combined.
E. A and C combined.
F. A, B, and C combined.

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.

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]

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]

Reply via email to