Re: htaccess limits
Paul <[email protected]> Fri, 8 May 2026 14:20:02 -0400
| Newsgroups | gmane.comp.apache.user |
|---|---|
| Organization | Stormy Weather |
| Message-ID | <[email protected]> |
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]
>