A Blog for the Designers of the Web by one of the Founders of the Internet

Why use a captcha’s when you can Xortify?


For sometime I have been developing a honey pot solution to some human rights abuse we detected in the XOOPS website wells. This solution is called Xortify! Just for fortification of your site, we had a lot of posts start happening on the XOOPS forum about ‘captchas’ being beat by a bot but this was also with people with Recaptcha from Google that is fool proof. So in the research on the source of what was breeching the captcha’s to sign up most of the accounts on the site in a day, it was discovered that this was being done by hand through a form of slavery called Captcha Sweat Shops.

What is a captcha sweat shop? Well essentially it is someone either trafficked in slavery or paid less than $1.00 a day to sign up and record the username, password and URL in a database of a site, this is often happening in places like India, China, Brazil and other countries for a solution to breeching captcha technology for spamming and harvesting of a website. So this is when I started to work on an Open Source floating cloud solution for this problem. This is hosted at www.xortify.com which you can see form the users that are logging in an out is quiet busy, in fact the domain gets over 75 Million hits a month currently.

How does Xortify Work? Well there is a client for the cloud for ImpressCMS and XOOPS currently – This client talks to the cloud at xortify.com and shares particular user information over a secure username and password system, that provides intel from several honeypots on the web namely – Stop Forum Spam and Project Honeypot as well as maintaining a ban list itself. This combination of not one but 3 honeypots, which is the ban list on xortify.com as well as the ban list on the other two mentioned prevents nearly 99% of maluser use of the site, preventing the persecution of data entry slaves in 3rd world and the less privileged countries.

There are the following API Ports on Xortify.com:

  • JSON API
  • XML API
  • SERIALISATION API
  • SOAP

So how does it work? XOOPS has a system of hooking stratum much like the way Windows works called a ‘preloader’ in the Xortify system there are 3 points of this hooking stratum which are used these are the events – eventCoreIncludeCommonStart(), eventCoreIncludeCommonEnd($args), eventCoreHeaderCacheEnd($args), eventCoreFooterEnd($args) & eventCoreHeaderAddmeta($args). In eventCoreIncludeCommonStart()  of modules/xortify/preloader/core.php there is a routine that runs the include file which can be used at this point of the hook by including the file in the preloader section in earlier versions of XOOPS where the trigger event call normally appears which has called basically the following file: modules/xortify/include/pre.loader.mainfile.php.

In this file pre.loader.mainfile.php the following 2 lines appear which is calling a precheck in the providers:

include_once (XOOPS_ROOT_PATH.'/modules/xortify/providers/providers.php');
$check = new Providers('precheck');

What is a provider? Well this is the system of providers or checks that can be used in xortify, in the XOOPS Client you can select which ones of these run, by default all except the one that requires the spider module to be installed is selected. So if you look at the provider folder in /modules/xortify/providers/ you will find the following paths which contain straps for the provider to be called as per the boot strapping class of the providers which is seen being included in the code above.

The following providers exist in the provider folder:

  • modules/xortify/providers/projecthoneypot.org/post.loader.php
  • modules/xortify/providers/protector/footer.post.loader.php
  • modules/xortify/providers/protector/header.post.loader.php
  • modules/xortify/providers/spiders/post.loader.php
  • modules/xortify/providers/stopforumspam.com/post.loader.php
  • modules/xortify/providers/xortify/footer.post.loader.php
  • modules/xortify/providers/xortify/header.post.loader.php
  • modules/xortify/providers/xortify/post.loader.php
  • modules/xortify/providers/xortify/pre.loader.php

To better understand how these are called you will have to look at the file modules/xortify/providers/providers.php which is the strapping for these routines. This has been added this way so it is easy to add other providers for a tailor made security solution and fortification of a website. Now before we go to much further lets look at the modules/xortify/class folder and the files within it. The only file which is a tradition handler class is the one for the database interaction with the log, this is log.php the rest are classes for interacting with the cloud and APIs these files we will look at now just quickly.

The files in module/xortify/class for interacting with the cloud are the following:

  • modules/xortify/class/curl.php (cURL Method with JSON Package)
  • modules/xortify/class/curlserialised.php (cURL Method with Serialized Package)
  • modules/xortify/class/curlxml.php (cURL Method with XML Package)
  • modules/xortify/class/json.php (wGet Method with JSON Package)
  • modules/xortify/class/soap.php (SOAP Method with SOAP Package)
  • modules/xortify/class/wgetserialised.php (wGet Method with Serialized Package)
  • modules/xortify/class/wgetxml.php (wGet Method with XML Package)

Only one instance of any of these 7 files is loaded at one time depending on the method you have selected in the preference, Xortify will best detect what is supported in your installation and select when you install, however you may have to adjust this. Each routine has a class named appropriately which contain inclusive and only the following functions:

  1. public function getServers()
    This will return from the cloud all supported servers on the xortify floating cloud.
  2. public function sendBan($comment, $category_id = 2, $ip=false)
    This will send a ban to the cloud to be banned, with comment array consisting of ID and comment as well as category of ban.
  3. public function checkSFSBans($ipdata)
    This will check for a stop forum spam ban and return the information.
  4. public function checkPHPBans($ipdata)
    This will check for a Project Honeypot Ban and return the information.
  5. public function getBans()
    This will either queue for the bans to be returned later on or return the bans now from the xortify ban list.
  6. public function retrieveBans()
    This will return the bans from the xortify cloud it is currently being served from.
  7. public function checkBanned($ipdata)
    This will check for a ban on the xortify cloud ban list in the last 3 months.

The $ipdata field in these routines is returned by the associative array of the routine found in modules/xortify/include/functions.php called xortify_getIPData($ip=false); this routine or function has several sub functions for finding the actual IP address and other information. You can of course construct this yourself but check out this routine anyway so you know what needs to be passed.

So lets look at the retrieveBans() function. It is basically the following code in curl.php:

function __construct() {
   
    $module_handler =& xoops_gethandler(‘module’);
    $config_handler =& xoops_gethandler(‘config’);
    if (!isset($GLOBALS[‘xortifyModule’])) $GLOBALS[‘xortifyModule’] = $module_handler->getByDirname(‘xortify’);
    if (!isset($GLOBALS[‘xortifyModuleConfig’])) $GLOBALS[‘xortifyModuleConfig’] = $config_handler->getConfigList($GLOBALS[‘xortifyModule’]->mid());
   
    $this->curl_xoops_username = $GLOBALS[‘xortifyModuleConfig’][‘xortify_username’];
    $this->curl_xoops_password = md5($GLOBALS[‘xortifyModuleConfig’][‘xortify_password’]);
    $this->refresh = $GLOBALS[‘xortifyModuleConfig’][‘xortify_records’];

    if (!$ch = curl_init(XORTIFY_CURL_API)) {
        trigger_error(‘Could not intialise CURL file: ‘.XORTIFY_CURL_API);
        return false;
    }
    $cookies = XOOPS_VAR_PATH.’/cache/xoops_cache/authcurl_’.md5(XORTIFY_CURL_API).’.cookie’;

    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $GLOBALS[‘xortifyModuleConfig’][‘curl_connecttimeout’]);
    curl_setopt($ch, CURLOPT_TIMEOUT, $GLOBALS[‘xortifyModuleConfig’][‘curl_timeout’]);
    curl_setopt($ch, CURLOPT_COOKIEJAR, $cookies);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_USERAGENT, XORTIFY_USER_AGENT);
    $this->curl_client =& $ch;           
}

function retrieveBans() {
    if (!empty($this->curl_client))
        switch (XOOPS_CURL_LIB){
        case “PHPCURL”:
            try {
                curl_setopt($this->curl_client, CURLOPT_POSTFIELDS, ‘bans=’.json_encode(array(“username”=> $this->curl_xoops_username, “password”=> $this->curl_xoops_password,  “records”=> $this->refresh)     ) );
                $data = curl_exec($this->curl_client);
                curl_close($this->curl_client);               
                $result = xortify_obj2array(json_decode($data));
            }       
            catch (Exception $e) { trigger_error($e); }
        }
    return $result;
}

This is essentially calling the following URL through a cURL instance, it is the same with the wGet method which is like this URL: http://xortify.com/curl/?bans={%22username%22:%22testuser%22,%22password%22:%225f4dcc3b5aa765d61d8327deb882cf99%22,%22records%22:1200} this is returning a json string of ban’s currently held on the cloud which if you click on the link will look similar to:

image

This is then picked up in the provider from calling this routine in the providers modules/xortify/providers/xortify/footer.post.loader.php or modules/xortify/providers/xortify/header.post.loader.php which is then cached and stored for a period of time with-in the following routine of the provider this function is called only if the xortify provider is turned on in the preferences:

require_once( XOOPS_ROOT_PATH.’/modules/xortify/class/’.$GLOBALS[‘xortifyModuleConfig’][‘protocol’].’.php’ );
$func = strtoupper($GLOBALS[‘xortifyModuleConfig’][‘protocol’]).’XortifyExchange’;
$soapExchg = new $func;
$result = $soapExchg->retrieveBans();

Other providers behave a little bit differently, for example the provider for Project Honeypot modules/xortify/providers/projecthoneypot.org/post.loader.php which does the following.

Between line 38 – 49 the following code appears:

if (!$ipdata = XoopsCache::read(‘xortify_php_’.sha1($_SERVER[‘REMOTE_ADDR’].(isset($_SERVER[‘HTTP_X_FORWARDED_FOR’])?$_SERVER[‘HTTP_X_FORWARDED_FOR’]:”).$uid.$uname.$email))) {
    $ipdata = xortify_getIPData(false);
    XoopsCache::write(‘xortify_php_’.sha1($_SERVER[‘REMOTE_ADDR’].(isset($_SERVER[‘HTTP_X_FORWARDED_FOR’])?$_SERVER[‘HTTP_X_FORWARDED_FOR’]:”).$uid.$uname.$email), $ipdata, $GLOBALS[‘xortifyModuleConfig’][‘xortify_ip_cache’]);
}

if (isset($ipdata[‘ip4’]))
    if ($ipdata[‘ip4’]==$GLOBALS[‘xoopsConfig’][‘my_ip’])
        return false;
       
if (isset($ipdata[‘ip6’]))
    if ($ipdata[‘ip6’]==$GLOBALS[‘xoopsConfig’][‘my_ip’])
        return false;

This retrieves the IP information or gets it for the first time and caches it, also checking by the $GLOBALS[‘xoopsConfig’][‘my_ip’] that it isn’t the registered webmasters IP Address if they have a static one which in that case the routine in aborted, see the if statements ending in return false;

Then it is seen if a cache file exists for the result of checking for a Project Honeypot information exists or not, with the clause at line 50/51:

$checked = XoopsCache::read(‘xortify_php_’.sha1($ipdata[‘uname’].$ipdata[’email’].(isset($ipdata[‘ip4’])?$ipdata[‘ip4’]:””).(isset($ipdata[‘ip6’])?$ipdata[‘ip6’]:””).(isset($ipdata[‘proxy-ip4’])?$ipdata[‘proxy-ip4’]:””).(isset($ipdata[‘proxy-ip4’])?$ipdata[‘proxy-ip6’]:””).$ipdata[‘network-addy’]));

If $checked isn’t an array then it means the data on this IP hasn’t been checked in the IP to Live time of the cache file which then does the following at lines 56 – 100:

require_once( XOOPS_ROOT_PATH.’/modules/xortify/class/’.$GLOBALS[‘xortifyModuleConfig’][‘protocol’].’.php’ );
$func = strtoupper($GLOBALS[‘xortifyModuleConfig’][‘protocol’]).’XortifyExchange’;
ob_start();
$soapExchg = new $func;
$result = $soapExchg->checkPHPBans($ipdata);
ob_end_flush();

Once the cloud class has been called with the checkPHPBans() function the array which is returned if successful on the call by the ‘success’ being flag with a TRUE value the ban is checked by the preferences specified in the config of the module to the degree of baniness you want it to work at this is the octeta, octetb & octetc variables returned by the cloud which is being checked on line 53 – 65.

if (is_array($result)) {
    if ($result[‘success’]==true)
        if (($result[‘octeta’]<=$GLOBALS[‘xortifyModuleConfig’][‘octeta’]&&$result[‘octetb’]>$GLOBALS[‘xortifyModuleConfig’][‘octetb’]&&$result[‘octetc’]>=$GLOBALS[‘xortifyModuleConfig’][‘octetc’])) {                      

The file cache on the ban information is then stored this is for any other queries of the ip information over the course of the user browsing the site so the cloud isn’t queried all the time and the site load quickly using a file cache. Line 71.

            XoopsCache::write(‘xortify_php_’.sha1($ipdata[‘uname’].$ipdata[’email’].(isset($ipdata[‘ip4’])?$ipdata[‘ip4’]:””).(isset($ipdata[‘ip6’])?$ipdata[‘ip6’]:””).(isset($ipdata[‘proxy-ip4’])?$ipdata[‘proxy-ip4’]:””).(isset($ipdata[‘proxy-ip4’])?$ipdata[‘proxy-ip6’]:””).$ipdata[‘network-addy’]), array_merge($result, array(‘ipdata’ => $ipdata)), $GLOBALS[‘xortifyModuleConfig’][‘xortify_ip_cache’]);

A Ban that has been made is now notified to the Xortify Cloud with the sendBan() function in the class Exchange, this is so the xortify cloud reflect activity found on the Project Honeypot cloud. Lines 75-87.

            $result = $soapExchg->sendBan(_XOR_BAN_PHP_TYPE.”\n”.
                                          _XOR_BAN_PHP_OCTETA.’ ‘.$result[‘octeta’].’ <= ‘ . $GLOBALS[‘xortifyModuleConfig’][‘octeta’].”\n”.
                                          _XOR_BAN_PHP_OCTETB.’ ‘.$result[‘octetb’].’ > ‘ . $GLOBALS[‘xortifyModuleConfig’][‘octetb’].”\n”.
                                          _XOR_BAN_PHP_OCTETC.’ ‘.$result[‘octetc’].’ >= ‘ . $GLOBALS[‘xortifyModuleConfig’][‘octetc’].”\n”, 5, $ipdata);
                                         
            $log_handler = xoops_getmodulehandler(‘log’, ‘xortify’);
            $log = $log_handler->create();
            $log->setVars($ipdata);
            $log->setVar(‘provider’, basename(dirname(__FILE__)));
            $log->setVar(‘action’, ‘banned’);
            $log->setVar(‘extra’, _XOR_BAN_PHP_OCTETA.’ ‘.$result[‘octeta’].’ <= ‘ . $GLOBALS[‘xortifyModuleConfig’][‘octeta’].”\n”.
                                  _XOR_BAN_PHP_OCTETB.’ ‘.$result[‘octetb’].’ > ‘ . $GLOBALS[‘xortifyModuleConfig’][‘octetb’].”\n”.
                                  _XOR_BAN_PHP_OCTETC.’ ‘.$result[‘octetc’].’ >= ‘ . $GLOBALS[‘xortifyModuleConfig’][‘octetc’]);
           

After the cloud has been notified a $_SESSION vairable is set as well as a cookie that the link is banned and the person is redirected to http://yoursite.com/banned.php for a ban notice to be displayed.

            $_SESSION[‘xortify’][‘lid’] = $log_handler->insert($log, true);
            XoopsCache::write(‘xortify_core_include_common_end’, array(‘time’=>microtime(true)), $GLOBALS[‘xortifyModuleConfig’][‘fault_delay’]);
            $_SESSION[‘xortify’][‘lid’] = $lid;
            setcookie(‘xortify’, array(‘lid’ => $lid), time()+3600*24*7*4*3);
            header(‘Location: ‘.XOOPS_URL.’/banned.php’);
            exit(0);

In the event that the routine of octets didn’t display a ban the file cache on the Project Honeypot information is then stored this is for any other queries of the ip information over the course of the user browsing the site so the cloud isn’t queried all the time and the site load quickly using a file cache. Line 99 – 100.

$GLOBALS[‘xortify_pass’] = true;
XoopsCache::write(‘xortify_php_’.sha1($ipdata[‘uname’].$ipdata[’email’].(isset($ipdata[‘ip4’])?$ipdata[‘ip4’]:””).(isset($ipdata[‘ip6’])?$ipdata[‘ip6’]:””).(isset($ipdata[‘proxy-ip4’])?$ipdata[‘proxy-ip4’]:””).(isset($ipdata[‘proxy-ip4’])?$ipdata[‘proxy-ip6’]:””).$ipdata[‘network-addy’]), array_merge($result, array(‘ipdata’ => $ipdata)), $GLOBALS[‘xortifyModuleConfig’][‘xortify_seconds’]);

This is essentially how all the providers work, checking a constraint by the setting off the cloud then banning if necessary or allowing past depending on the setting on the preferences which seem to by default keep out of 99% of the attacks, you can get the latest clients for xortify as well as copies of the server cloud to set up your own cloud in PHP code from the following URL http://code.google.com/p/chronolabs.

Leave a comment