The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

HTTP::ProxyTest - Reject an HTTP request if passed via an open proxy

SYNOPSIS

    use HTTP::ProxyTest;

    proxytest(
        -nmap       =>  '/usr/local/bin/nmap',
        -whitelist  =>  '/usr/local/etc/ProxyTest_whitelist',
        -log        =>  '/var/log/open_proxy.log',
    );

DESCRIPTION

Robots that send comment spam are often hidden behind anonymous open proxy servers. You can use HTTP::ProxyTest to look for open proxies on-the-fly and prevent such spam robots from submitting their crap. The module is particularly useful if you don't want to bother your web site visitors with CAPTCHAs etc.

HTTP::ProxyTest tests certain ports of REMOTE_ADDR that are often used for anonymous open proxies, and denies access if an open proxy is found, i.e. it responds with status "403 Forbidden" and exits. The module was designed to make use of the Nmap security scanner (http://nmap.org/) in order to speed up things and/or increase the number of ports to be considered for testing. Consequently, if Nmap is currently not available to you, you are advised to download and install that program.

The strong point of HTTP::ProxyTest, compared to other similar CPAN modules (see "SEE ALSO"), is its speed. Since Nmap limits the number of ports to test, HTTP::ProxyTest can do on-the-fly testing fast enough to cover quite a few proxy port candidates, without causing any significant response delay. The same seems not to be true for other modules.

Arguments

Below are the arguments that can be passed the proxytest() function, which by the way is the only function of HTTP::ProxyTest that you are supposed to call from outside the module. proxytest() takes hash style key=>value arguments (see "SYNOPSIS"). All the arguments are optional.

-nmap

Path to the nmap executable; no value by default.

If -nmap is set, HTTP::ProxyTest will test those -primary ports that Nmap reports to be either open or filtered, while it will only test those -secondary ports that Nmap reports to be open.

If -nmap is not set, HTTP::ProxyTest will test all the -primary ports and skip the -secondary ports.

-primary

Reference to an array of ports where the risk of carrying an open proxy is not insignificant. Default value:

    [ 80, 3128, 8080 ]
-secondary

Reference to an array of ports which are less likely, compared to the -primary ports, to carry an open proxy. Default value:

    [ 808, 6588, 8000, 8088 ]
-test_url

Web address used for proxy testing; defaults to 'http://gunnar.cc/proxy_test.txt', which is the address to a tiny text file on my own server. Even if that address works fine when I'm writing this, there is no guarantee that it will keep working for all time, so you are recommended to set -test_url to a resource that you control. Choose a URL to a tiny page on a reliable server which includes the status line 200 OK in the responses.

-content_substr

A string that shall be included in the content string of the response; defaults to 'y4dWP:a7w'. To prevent false positives, HTTP::ProxyTest will not report that a host carries an open proxy, unless it has confirmed an occurrence of -content_substr in the response content string.

Obviously, if you set -test_url, you will most likely need to set -content_substr as well.

-timeout

When doing proxy testing, HTTP::ProxyTest expects to establish a server connection within -timeout seconds after a request, or else the request is aborted. Defaults to 4.

-whitelist

Path to a DBM database with IP addresses of hosts that passed the proxy tests during the last week; no value by default. If you set -whitelist, HTTP::ProxyTest will maintain the database and skip testing for hosts in the 'whitelist'.

-log

Path to a text file where information about requests from hosts with open proxies is logged; no value by default.

-log_maxbytes

Maximum size in bytes of the -log file; defaults to 1_000_000. If -log is set, and when the max size is touched, HTTP::ProxyTest halves the file size by removing the oldest entries.

EXAMPLES

Perl web apps

After having adapted the "SYNOPSIS" code, you can simply insert it e.g. before any form generating or form data processing code portion of a Perl program. To shorten the code to be inserted in various programs, you can place a wrapper in one of the @INC directories.

    # proxytest.pl
    use HTTP::ProxyTest;
    proxytest(
        -nmap       =>  '/usr/local/bin/nmap',
        -whitelist  =>  '/usr/local/etc/ProxyTest_whitelist',
        -log        =>  '/var/log/open_proxy.log',
    );
    1;

Now you can invoke HTTP::ProxyTest by just saying:

    require 'proxytest.pl';

PHP web apps

This example of how to invoke HTTP::ProxyTest from PHP begins with this script, located in one of the PHP include_path directories:

    <?php
    // proxytest.php
    function proxytest() {
        $args = implode(' ', array(
            getenv('REMOTE_ADDR'),
            getenv('HTTP_HOST'),
            getenv('REQUEST_URI'),
        ));
        exec('/path/to/proxytest.pl ' . $args, $error);
        if ( count($error) ) {
            header('HTTP/1.0 403 Forbidden');
            echo ( implode( "\n", array_slice($error, 3) ) );
            exit;
        }
    }
    proxytest();
    ?>

Then we add some code to the wrapper and make it an executable Perl script.

    #!/usr/bin/perl
    # proxytest.pl
    use HTTP::ProxyTest;
    if ( $ENV{_} and $ENV{_} eq '/path/to/proxytest.php' ) {
        @ENV{ qw/REMOTE_ADDR HTTP_HOST REQUEST_URI/ } = @ARGV;
    }
    proxytest(
        -nmap       =>  '/usr/local/bin/nmap',
        -whitelist  =>  '/usr/local/etc/ProxyTest_whitelist',
        -log        =>  '/var/log/open_proxy.log',
    );
    1;
 

Finally the single line call from a PHP program:

    include 'proxytest.php';

DEPENDENCIES

This module is dependent on the libwww-perl set of modules.

Also, even if it's possible to use HTTP::ProxyTest without access to the Nmap security scanner, we'd better consider Nmap to be a 'soft dependency', a.k.a. strong recommendation.

CAVEAT

In case of HTTP::ProxyTest being invoked via a server wide wrapper, and the web server may be run as more than one user (e.g. because of Apache suEXEC), you should pay attention to the permissions of the DBM and log files. You may want to make sure that those files are 'world writable'.

AUTHOR, COPYRIGHT AND LICENSE

    Copyright (c) 2010-2011 Gunnar Hjalmarsson
    http://www.gunnar.cc/cgi-bin/contact.pl

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

SEE ALSO

HTTP::ProxyCheck, HTTP::CheckProxy