diff --git a/inc/config.php b/inc/config.php index a83edecd..092b1005 100644 --- a/inc/config.php +++ b/inc/config.php @@ -18,7 +18,8 @@ 'cookies' => Array(), 'error' => Array(), 'dir' => Array(), - 'mod' => Array() + 'mod' => Array(), + 'spam' => Array() ); // Database stuff @@ -92,6 +93,7 @@ $config['error']['locked'] = 'Thread locked. You may not reply at this time.'; $config['error']['nopost'] = 'You didn\'t make a post.'; $config['error']['flood'] = 'Flood detected; Post discared.'; + $config['error']['spam'] = 'Your request looks automated; Post discared.'; $config['error']['unoriginal'] = 'Unoriginal content!'; $config['error']['muted'] = 'Unoriginal content! You have been muted for %d seconds.'; $config['error']['youaremuted'] = 'You are muted! Expires in %d seconds.'; @@ -282,6 +284,36 @@ $config['mod']['link_lock'] = '[Lock]'; $config['mod']['link_unlock'] = '[-Lock]'; + // Spam filter + $config['spam']['hidden_inputs_min'] = 4; + $config['spam']['hidden_inputs_max'] = 12; + // These are fields used to confuse the bots. Make sure they aren't actually used by Tinyboard, or it won't work. + $config['spam']['hidden_input_names'] = Array( + 'user', + 'username', + 'login', + 'search', + 'q', + 'url', + 'firstname', + 'lastname', + 'text', + 'message' + ); + // Always update this when adding new valid fields to the post form, or EVERYTHING WILL BE DETECTED AS SPAM! + $config['spam']['valid_inputs'] = Array( + 'hash', + 'board', + 'thread', + 'mod', + 'name', + 'email', + 'subject', + 'post', + 'body', + 'password' + ); + // A small file in the main directory indicating that the script has been ran and the board(s) have been generated. // This keeps the script from querying the database and causing strain when not needed. $config['has_installed'] = '.installed'; diff --git a/inc/functions.php b/inc/functions.php index 854b0397..8b6ac65a 100644 --- a/inc/functions.php +++ b/inc/functions.php @@ -634,6 +634,105 @@ } } + function createHiddenInputs() { + global $config; + + $inputs = Array(); + + shuffle($config['spam']['hidden_input_names']); + $hidden_input_names_x = 0; + + $input_count = rand($config['spam']['hidden_inputs_min'], $config['spam']['hidden_inputs_max']); + for($x=0;$x<$input_count;$x++) { + if(rand(0, 2) == 0 || $hidden_input_names_x < 0) { + // Use an obscure name + $name = substr(base64_encode(sha1(rand())), 0, rand(2, 40)); + } else { + // Use a pre-defined confusing name + $name = $config['spam']['hidden_input_names'][$hidden_input_names_x++]; + if($hidden_input_names_x > count($config['spam']['hidden_input_names'])) + $hidden_input_names_x = -1; + } + + if(rand(0, 2) == 0) { + // Value must be null + $inputs[$name] = ''; + } elseif(rand(0, 4) == 0) { + // Numeric value + $inputs[$name] = rand(0, 100); + } else { + // Obscure value + $inputs[$name] = substr(base64_encode(sha1(rand())), 0, rand(2, 40)); + } + } + + $content = ''; + foreach($inputs as $name => $value) { + $content .= '' . "\n "; + } + + // Create a hash to validate it after + // This is the tricky part. + + // First, sort the keys in alphabetical order (A-Z) + ksort($inputs); + + $hash = ''; + + // Iterate through each input + foreach($inputs as $name => $value) { + $hash .= $name . '=' . $value; + } + + // Add a salt to the hash + $hash .= $config['cookies']['salt']; + + // Use SHA1 for the hash + $hash = sha1($hash); + + // Append it to the HTML + $content .= ''; + + return $content; + } + + function checkSpam() { + global $config; + + if(!isset($_POST['hash'])) + return true; + + $hash = $_POST['hash']; + + // Reconsturct the $inputs array + $inputs = Array(); + + foreach($_POST as $name => $value) { + if(in_array($name, $config['spam']['valid_inputs'])) + continue; + + $inputs[$name] = $value; + } + + // Sort the inputs in alphabetical order (A-Z) + ksort($inputs); + + $_hash = ''; + + // Iterate through each input + foreach($inputs as $name => $value) { + $_hash .= $name . '=' . $value; + } + + // Add a salt to the hash + $_hash .= $config['cookies']['salt']; + + // Use SHA1 for the hash + $_hash = sha1($_hash); + + return $hash != $_hash; + } + function buildIndex() { global $board, $config; sql_open(); @@ -648,6 +747,7 @@ $content['pages'] = $pages; $content['pages'][$page-1]['selected'] = true; $content['btn'] = getPageButtons($content['pages']); + $content['hidden_inputs'] = createHiddenInputs(); @file_put_contents($filename, Element('index.html', $content)) or error("Couldn't write to file."); if(isset($md5) && $md5 == md5_file($filename)) { @@ -837,6 +937,7 @@ 'index' => $config['root'], 'id' => $id, 'mod' => $mod, + 'hidden_inputs' => $content['hidden_inputs'] = createHiddenInputs(), 'return' => ($mod ? '?' . $board['url'] . $config['file_index'] : $config['root'] . $board['uri'] . '/' . $config['file_index']) )); diff --git a/mod.php b/mod.php index 034e18a8..86ae4b5d 100644 --- a/mod.php +++ b/mod.php @@ -327,6 +327,7 @@ $page['pages'] = getPages(true); $page['pages'][$page_no-1]['selected'] = true; $page['btn'] = getPageButtons($page['pages'], true); + $page['hidden_inputs'] = createHiddenInputs(); $page['mod'] = true; echo Element('index.html', $page); diff --git a/templates/index.html b/templates/index.html index 59ce3f54..f75b9015 100644 --- a/templates/index.html +++ b/templates/index.html @@ -13,6 +13,7 @@
+ {hidden_inputs} {mod?} diff --git a/templates/thread.html b/templates/thread.html index 19434be3..eae22720 100644 --- a/templates/thread.html +++ b/templates/thread.html @@ -12,7 +12,7 @@
{board[title]?{board[title]}}

{mod?Return to dashboard}

- + {hidden_inputs} {mod?}