(21.09.2013, 12:27)StefanT schrieb: [ -> ]Richtig, genau so sieht es aus.
Das beste Mittel ist eine individuelle Lösung. Denn das was standardmäßig vorhanden ist, wird von den Spammern schnell wieder umgangen.
Hallo Stefan,
eigentlich mag ich nicht meckern, ohne eine Lösungsidee anzubieten.
Was hältst Du von einer grundlegend veränderten Captcha-Funktion, bei der zum Hash eine zeitlich begrenzte TAN erzeugt wird?
Hier mal ein Codeschnipselchen ohne zugehörige Datenbank, dass man sicherlich anpassen kann:
Code:
<?php
/*-----------------------------------------------------------------------------
* Helper function(s)
*-----------------------------------------------------------------------------*/
/**Prints a beautified error message.
*/
if( !function_exists( '__DB_ERROR__' ))
{
function __DB_ERROR__( $pdo_err_msg, $line = 'No line specified' )
{
if( is_array( $pdo_err_msg ))
$pdo_err_msg = implode( ' --- ', $pdo_err_msg );
$errstr = <<<EOE
<div style="border:1px solid #808080;background-color:lightyellow;padding:5px;z-index:2;">
<div style="font-weight:bold;color:red;">
MySQL Error
</div>
<div style="color:#d00000;">
$pdo_err_msg<br />
Occured in line: $line
</div>
</div>
EOE;
return $errstr;
}
}
/*-----------------------------------------------------------------------------
* The Captcha/TAN class
*-----------------------------------------------------------------------------*/
/**
*/
class TAN
{
/**Database connection handle.
*/
private $dbh = NULL;
/**Database table name where the TANs are stored.
*/
private $table = 'captcha';
/**Minimum time in seconds to pass.
*/
private $minTime;
/**Maximum time in seconds to pass.
*/
private $maxTime;
/**Default constructor.
* @param $dbh Database handle. (PDO only)
* @param $minTime Minimum time in seconds to pass before the TAN will be valid. Must be positive (or 0). Default: 5
* @param $maxTime Maximum time in seconds to pass before the TAN will time out. Must be positive (or 0). Default: 300
* @note There is no explicit check of min and max time, i.e. someone could enter ludicrous large numbers in here. It
* is not checked, because we just don't want to set any limitations for you other than min time.
*/
public function __construct( $dbh, $minTime = 5, $maxTime = 300 )
{
$this->setDBH( $dbh );
// Validity check
if( is_numeric( $minTime ) && is_numeric( $maxTime ))
{
// Swap if wrong order
if( $minTime > $maxTime )
{
$this->minTime = max( 0, $maxTime ); // 0 is minium value
$this->maxTime = $minTime;
}
else
{
$this->minTime = max( 0, $minTime ); // 0 is minimum value
$this->maxTime = ( $minTime == $maxTime ) ? $maxTime + 1 : $maxTime;
}
}
}
/**Get minimum time to pass.
*/
public function getMinTime()
{
return $this->minTime;
}
/**Get maximum time to pass.
*/
public function getMaxTime()
{
return $this->maxTime;
}
/**Check for valid database connection before accessing it.
*/
private function hasValidDBH()
{
return NULL != $this->dbh;
}
/**Set database handle.
* @param $dbh Database handle. (PDO only)
* @return TRUE if the database handle was set, FALSE otherwise.
*/
public function setDBH( $dbh )
{
if( $dbh instanceof PDO )
{
$this->dbh = $dbh;
return true;
}
return false;
}
/**Remove old captchas from the database.
*/
public function clearTANList()
{
if( $this->hasValidDBH() )
{
// Delete old captcha codes ...
$this->dbh->exec( "DELETE FROM {$this->table} WHERE `date_inserted` < DATE_SUB( NOW(), INTERVAL 1 DAY );" );
// .. and OPTIMIZE TABLE afterwards
$this->dbh->query( "OPTIMIZE TABLE {$this->table}" );
}
}
/**Insert CAPTCHA into DB.
* @return The hash refering to the newly created key in the database. (reference ID, refID)
*/
public function insertTAN()
{
if( !$this->hasValidDBH() )
{
return false;
}
// Random part (5 chars) from a hash as key
$key = substr( sha1( microtime() ), mt_rand( 0, 34 ), 5 );
$hash = str_shuffle( sha1( $key ));
// Insert
$sth = $this->dbh->prepare( "INSERT INTO {$this->table} ( `refid`, `riddle` ) VALUES ( :hash, :key )" );
if( !$sth || !$sth->execute( array( ':hash' => "$hash", ':key' => "$key" )))
echo __DB_ERROR__( $sth->errorInfo(), __LINE__ );
// Return created hash for reference
return $hash;
}
/**Check whether given reference ID refers to an entry in the database.
* @param $refID Reference ID to search in the database.
* @return FALSE when there is no valid entry for given ID, -1 if isValidTAN() was called too fast after insertTAN() and TRUE otherwise.
* @see insertTAN()
*/
public function isValidTAN( $refID )
{
if( !$this->hasValidDBH() )
{
return false;
}
$sth = $this->dbh->prepare( "SELECT `date_inserted`, NOW() as `used_at` FROM {$this->table} WHERE `refid` = :refid AND `date_used` IS NULL" );
if( !$sth || !$sth->execute( array( ':refid' => $refID )))
echo __DB_ERROR__( $sth->errorInfo(), __LINE__ );
// Get result set
$rs = $sth->fetch( PDO::FETCH_ASSOC );
// Nothing found OR TAN timed out => $refID not valid
if( $rs === FALSE || count( $rs ) == 0 ||
(( strtotime( $rs['used_at'] ) - strtotime( $rs['date_inserted'] )) > $this->maxTime ))
return FALSE;
// Avoid a solved captcha being used twice
$this->removeTAN( $refID );
// If entry is younger than 5 seconds ==> SPAM
if(( strtotime( $rs['used_at'] ) - strtotime( $rs['date_inserted'] )) < $this->minTime )
return -1;
// Finally a valid captcha
return TRUE;
}
/**Get metadata of a TAN, e.g. for debugging or logging issues.
* @param $refID Reference ID to get metadata from the database.
* @return FALSE when given reference ID is invalid, metadata otherwise.
*/
public function getMetaData( $refID )
{
if( !$this->hasValidDBH() )
{
return false;
}
$sth = $this->dbh->prepare( "SELECT `date_inserted`, `date_used`, `riddle` FROM {$this->table} WHERE `refid` = :refid" );
if( !$sth || !$sth->execute( array( ':refid' => $refID )))
echo __DB_ERROR__( $sth->errorInfo(), __LINE__ );
// Get result set
$rs = $sth->fetch( PDO::FETCH_ASSOC );
// Nothing found => $refID not valid
if( $rs === FALSE || count( $rs ) == 0 )
return FALSE;
return $rs;
}
/**Remove CAPTCHA from DB specified by given refid.
* @param $refID Reference ID to delete from the database.
*/
protected function removeTAN( $refID )
{
if( !$this->hasValidDBH() )
{
return false;
}
$sth = $this->dbh->prepare( "UPDATE {$this->table} SET `date_used` = NOW() WHERE `refid` = :refid" );
if( !$sth || !$sth->execute( array( ':refid' => $refID )))
echo __DB_ERROR__( $sth->errorInfo(), __LINE__ );
}
}
?>
Wie gesagt, ich biete ein Opensource-Gästebuch an, bei dem ich ganz auf Captchas verzichte - ist seit 1 Jahr in der 3.20er-Version bei zig Leuten im Einsatz, die 2.05er war trotz Captchas zugespammt in Minuten, auf der 3.20er habe ich bis jetzt nur 3 Spam-Einträge gefunden - die vermutlich händisch eingetragen wurden...
Hallo Frostschutz,
ich habe normal in dem Forum ca. 650 MB Traffic; die Spambots erhöhten den Traffic auf fast 18 GB! im Monat (Kein Tippfehler, ich schrieb Gigabyte.) Das Serverlog vom Indianer war 650 MB groß nur mit Ips und Zugriffen auf die Dateien.
Wenn ich nun die kompletten Requests mitlogge, muss ich für die Logfiles eine eigene Festplatte einbauen.
Das ist mir die XRumer-SpamschleuderGmbH & Co. KG nicht wert, da knacke ich eher deren eigene Software, programmiere die um auf Spamserver identifizieren und zuspammen, und stelle die als Freeware für Internetprovider zum Download ins Netz... ;-)