Topic: [release] Yet another spam blocker (YASB - a non captcha alternative)

Blocks comment spam with non-captcha methods.
The extension adds 3 options to PunBB's administrative settings panel:

1. Stop posting over proxies of the  transparent type.
This proxy type is usually abused by spammers. It is preferred over the anonymous type, probably because the latter type often is slow and only accessible through web-interfaces. This method of comment spam blocking works surprisingly good in many cases - and still allows forum visitors to use anonymous proxies without limitations. Almost the same method is used by Google to protect their servers, in that you are not allowed to start a search without solving a captcha. Remeber that the anonymous type is not blocked, only the transparent one.

2. Prevent posting from clients that do not send the accept-language header (every bowser sends this header, but spammers sometimes hide it completely). 

3. Insert a spammer trap in post.php (a hidden dummy form) - only bots can see it. Thus most bots cannot abuse the post form. They are blocked before they reach the real  form. Only the most recent IP's from bots that used the hidden form are stored- the oldest are deleted.

This extension might be useful for those who are looking for a captcha alternative or for those looking for additional spam protection.

You would need to create two files as follows.
- the yasb extension folder should contain manifest.xml with:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE extension SYSTEM "ext-1.0.dtd">

<!--
/**
 * Yet another spam blocker (YASB - a non captcha alternative)
 *
 * @package yasb
 */
-->

    <extension engine="1.0">
    <id>yasb</id>
    <title>Yet another spam blocker (YASB - a non captcha alternative)</title>
    <version>1.1</version>
    <description>Blocks comment spam with non-captcha methods: 
    1. Stops posting over the transparent proxy type (this type is usually abused by spammers) but allows the anonymous web proxies maybe used by your forum visitors. 
    2. Prevents posting from  browsers not sending an accept-language header (every bowser sends this header, but spammers often try to hide the originating country).  
    3. Inserts a spammer trap through a hidden form in post.php - only bots can see it. Thus many bots are blocked from using the post form. Only the most recent IP's that used the hidden form are stored, so the blocked IP's will expire automatically. 
    </description>
    <author>NettiCat</author>
    <minversion>1.3</minversion>
    <maxtestedon>1.3.4</maxtestedon>




    <install>
        <![CDATA[
        $new_config = array(
            'o_yasb_block_proxy'        => '1',
            'o_yasb_block_missing_language'    => '1',
            'o_yasb_spamtrap'        => '1'
        );

        foreach($new_config as $conf_name => $conf_value)
        {
            if (!array_key_exists($conf_name, $forum_config))
            {
                $query = array(
                    'INSERT'    => 'conf_name, conf_value',
                    'INTO'        => 'config',
                    'VALUES'    => '\''.$conf_name.'\', \''.$conf_value.'\''
                );
                $forum_db->query_build($query) or error(__FILE__, __LINE__);
            }
        }

        ]]>
    </install>
    
    <uninstall>
        <![CDATA[
            $query = array(
            'DELETE'    => 'config',
            'WHERE'        => 'conf_name in (\'o_yasb_block_proxy\', \'o_yasb_block_missing_language\', \'o_yasb_spamtrap\')',
            );
            $forum_db->query_build($query) or error(__FILE__, __LINE__);
        ]]>
    </uninstall>

    <hooks>
        <hook id="po_start"><![CDATA[
            $fname = ".htbots"; //bot IP's file
            //is the IP an already known bot? compare with saved IP's
            if (file_exists($fname) && $forum_config['o_yasb_spamtrap'] == '1')         
            {
                $lines = file($fname);
                foreach($lines as $line)
                {
                    if(trim($line) == $_SERVER['REMOTE_ADDR']){
                        //known bot has gone into trap
                        echo "403 Forbidden - Your IP is listed as known spam bot\n";
                        header("HTTP/1.0 403 Forbidden");
                        exit;
                    }
                }
            }
            //yet unknown bot has gone into trap
            if (isset($_GET['trap']) && $forum_config['o_yasb_spamtrap'] == '1')
            {
                //read saved bot IP list
                if (file_exists($fname)) {
                    $lines = file($fname);
                }else{
                    $lines = array(); 
                }
                //trim list
                while(count($lines) > 100) {
                    array_shift($lines);
                }
                //add new bot IP
                array_push($lines, $_SERVER['REMOTE_ADDR']);
                //save updated list to public file
                $fhandle = fopen($fname,"w");
                    foreach($lines as $key => $value){
                        fwrite($fhandle,trim($value)."\n");
                }
                fclose($fhandle);
                echo "403 Forbidden - It seems you are a spam bot\n";
                header("HTTP/1.0 403 Forbidden");
                exit;
            }

            //check request headers
            $headers = getallheaders();
            $acceptlang = "";
            while (list ($header, $value) = each ($headers)) {
                if($forum_config['o_yasb_block_proxy'] == '1' && (stristr($header, 'FORWARD') == TRUE || stristr($header, 'PROX') == TRUE  || stristr($header, 'VIA') == TRUE
                    || stristr($header, 'REMOTE') == TRUE || stristr($header, 'CLIENT-IP') == TRUE)) {
                    echo "403 Forbidden - Posting via PROXY is not allowed, please use a direct connection (anti-spam protection)\n";
                    header("HTTP/1.0 403 Forbidden");
                    exit;
                }
                if(stristr($header, 'ACCEPT-LANGUAGE') == TRUE) {
                    $acceptlang = $value;
                }
            }
            //does accept language exist?
            if($forum_config['o_yasb_block_missing_language'] == '1' && $acceptlang == "") {
                echo "403 Forbidden - Missing browser accept language (anti-spam protection)\n";
                header("HTTP/1.0 403 Forbidden");
                exit;
            }
            ]]>
        </hook>

        <hook id="po_main_output_start, vt_qpost_output_start">
        <![CDATA[
            //create hidden form as bot trap
            ?>
            <!-- spam trap begin -->
            <div style='display: none;'>
            <form action='post.php?trap=1' method='post'><br>
            <input name='email' value='email'/><br>
            <input name='homepage' value='homepage'/><br>
            <input name='url' value='url'/><br>
            <textarea name='comment'></textarea><br>
            </form>
            </div>
            <!-- spam trap end -->
            <?php
        ]]>
    </hook>

        <hook id="aop_setup_validation">
            <![CDATA[
                if (!isset($form['yasb_block_proxy']) || $form['yasb_block_proxy'] != '1')
                    $form['yasb_block_proxy'] = '0';
                
                if (!isset($form['yasb_block_missing_language']) || $form['yasb_block_missing_language'] != '1')
                    $form['yasb_block_missing_language'] = '0';
                
                if (!isset($form['yasb_spamtrap']) || $form['yasb_spamtrap'] != '1')
                    $form['yasb_spamtrap'] = '0';
            ]]>
        </hook>

        <hook id="aop_setup_personal_fieldset_end">
            <![CDATA[
                    if (file_exists($ext_info['path'].'/lang/'.$forum_user['language'].'/'.$ext_info['id'].'.php'))
                        require $ext_info['path'].'/lang/'.$forum_user['language'].'/'.$ext_info['id'].'.php';
                    else
                        require $ext_info['path'].'/lang/English/'.$ext_info['id'].'.php';
                    $forum_page['group_count'] = $forum_page['item_count'] = 0;
                ?>
                <div class="content-head">
                    <h2 class="hn"><span><?php echo $lang_yasb['Setup'] ?></span></h2>
                </div>
                <fieldset class="frm-group group<?php echo ++$forum_page['group_count'] ?>">
                    <legend class="group-legend"><strong><?php echo $lang_yasb['Setup description legend'] ?></strong></legend>
                    <div class="sf-set set<?php echo ++$forum_page['item_count'] ?>">
                        <div class="sf-box checkbox">
                            <label for="fld<?php echo $forum_page['fld_count'] ?>">
                                <span><?php echo $lang_yasb['Proxy'] ?></span>
                                <?php echo $lang_yasb['Proxy help'] ?>
                            </label>
                            <span class="fld-input"><input type="checkbox" id="fld<?php echo ++$forum_page['fld_count'] ?>" name="form[yasb_block_proxy]" value="1" <?php if ($forum_config['o_yasb_block_proxy'] == '1') echo ' checked="checked"' ?>/></span>
                        </div>
                    </div>
                    <div class="sf-set set<?php echo ++$forum_page['item_count'] ?>">
                        <div class="sf-box checkbox">
                            <label for="fld<?php echo $forum_page['fld_count'] ?>">
                                <span><?php echo $lang_yasb['Language'] ?></span>
                                <?php echo $lang_yasb['Language help'] ?>
                            </label>
                            <span class="fld-input"><input type="checkbox" id="fld<?php echo ++$forum_page['fld_count'] ?>" name="form[yasb_block_missing_language]" value="1" <?php if ($forum_config['o_yasb_block_missing_language'] == '1') echo ' checked="checked"' ?>/></span>
                        </div>
                    </div>
                    <div class="sf-set set<?php echo ++$forum_page['item_count'] ?>">
                        <div class="sf-box checkbox">
                            <label for="fld<?php echo $forum_page['fld_count'] ?>">
                                <span><?php echo $lang_yasb['Trap'] ?></span>
                                <?php echo $lang_yasb['Trap help'] ?>
                            </label>
                            <span class="fld-input"><input type="checkbox" id="fld<?php echo ++$forum_page['fld_count'] ?>" name="form[yasb_spamtrap]" value="1" <?php if ($forum_config['o_yasb_spamtrap'] == '1') echo ' checked="checked"' ?>/></span>
                        </div>
                    </div>
                </fieldset>
                <?php

            ]]>
        </hook>
    </hooks>

</extension>



....and the lang/english folder should contain yasb.php with:

<?php
if (!defined('FORUM')) die();
$lang_yasb = array(
    'Setup'                => 'YASB Spam Protection',
    'Setup description legend'    => 'Configure Spam Protection',
    'Proxy'                => 'Block transparent proxies',
    'Proxy help'        => 'If checked, any attempt to post via a non-anonymous proxy is rejected.',
    'Language'            => 'Block posts with missing language header',
    'Language help'        => 'If checked, posts from clients that do not send the accept language header will be rejected.',
    'Trap'                => 'Activate spammer trap',
    'Trap help'            => 'If checked, a invisible dummy form is created - any bot that uses the form is added to a temporary IP blacklist and cannot access post.php'
)
?>

YASB spammer trap would create a bot-IP plain text file named .htbots in the forum folder. You might want to make it public/share otherwise deny access by defining a .htaccess rule e.g.

<FilesMatch "^\.htbots">
Order allow,deny
Deny from all
</FilesMatch>

Re: [release] Yet another spam blocker (YASB - a non captcha alternative)

I have just two little advice.

$fname = ".htbots";

You can create such a file in the 'cache' directory (one can be sure it's writeable):

$fname = FORUM_ROOT.'cache/.htbots';

And it's better to lock the file before writing, see the flock function description.

Re: [release] Yet another spam blocker (YASB - a non captcha alternative)

Parpalak wrote:

I have just two little advice

Thank you! This is my first work with the PHP language, and good advice is always welcome.  smile

I just applied your suggestions and release it as version 1.11,
but this time as zip file - in case there are users who have problems with creating the necessary files.

It is hosted here: http://www.megaupload.com/?d=4RJUVQCJ

Re: [release] Yet another spam blocker (YASB - a non captcha alternative)

Ok smile

Then I'll write one more thing. It's not important here (a file that contains 100 lines), but it can be a kind of small optimization for big files (~1Mb).

When you check the presence of a substring in a file, you can do this:

$file_contents = file_get_contents('/path/to/file')

if (strpos($file_contents, $substr) !== false) {
    // Found
    ...
}

Saving and loading:

file_put_contents('/path/to/file', implode("\n", $array_of_lines));

$array_of_lines = explode("\n", file_get_contents('/path/to/file'));

There is no "\n" at the end of elements in $array_of_lines (unlike the returned values of the file() function).

And the code is more compact when these functions are used.

Re: [release] Yet another spam blocker (YASB - a non captcha alternative)

Well, the explode function is really useful, also removing the evil \n
I looked for a function like that, but did no find yet. Thanks.

I  begin to realize that PHP probably is a very nice language, worth to spend much more time on.

Re: [release] Yet another spam blocker (YASB - a non captcha alternative)

LOLing at use of MegaUpload for File size: 4.03 KB

But thanks for this extension - the proxy blocking element in particular will be very handy.

7 (edited by dimkalinux 2009-10-22 05:57)

Re: [release] Yet another spam blocker (YASB - a non captcha alternative)

In my test extension that block bots on my forum i use this idea - block all posts with more then 15 links in post. Spam bots usually posts message with big counts of links. It`s work. Also you can configure max number of links for posts, but not separately by user groups.

You can find my ext — http://forum.lluga.net/fancy_stop_spam.tar.gz

Will be cool if you move you ext development to github, i think it not last idea for make better spam-fight in Punbb forums.