The version of vichan running on lainchan.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

anti-bot.php 7.8KB

12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
12 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
11 jaren geleden
12 jaren geleden
11 jaren geleden
11 jaren geleden
11 jaren geleden
12 jaren geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <?php
  2. /*
  3. * Copyright (c) 2010-2013 Tinyboard Development Group
  4. */
  5. defined('TINYBOARD') or exit;
  6. $hidden_inputs_twig = array();
  7. class AntiBot {
  8. public $salt, $inputs = array(), $index = 0;
  9. public static function randomString($length, $uppercase = false, $special_chars = false, $unicode_chars = false) {
  10. $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
  11. if ($uppercase)
  12. $chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  13. if ($special_chars)
  14. $chars .= ' ~!@#$%^&*()_+,./;\'[]\\{}|:<>?=-` ';
  15. if ($unicode_chars) {
  16. $len = strlen($chars) / 10;
  17. for ($n = 0; $n < $len; $n++)
  18. $chars .= mb_convert_encoding('&#' . mt_rand(0x2600, 0x26FF) . ';', 'UTF-8', 'HTML-ENTITIES');
  19. }
  20. $chars = preg_split('//u', $chars, -1, PREG_SPLIT_NO_EMPTY);
  21. $ch = array();
  22. // fill up $ch until we reach $length
  23. while (count($ch) < $length) {
  24. $n = $length - count($ch);
  25. $keys = array_rand($chars, $n > count($chars) ? count($chars) : $n);
  26. if ($n == 1) {
  27. $ch[] = $chars[$keys];
  28. break;
  29. }
  30. shuffle($keys);
  31. foreach ($keys as $key)
  32. $ch[] = $chars[$key];
  33. }
  34. $chars = $ch;
  35. return implode('', $chars);
  36. }
  37. public static function make_confusing($string) {
  38. $chars = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
  39. foreach ($chars as &$c) {
  40. if (mt_rand(0, 3) != 0)
  41. $c = utf8tohtml($c);
  42. else
  43. $c = mb_encode_numericentity($c, array(0, 0xffff, 0, 0xffff), 'UTF-8');
  44. }
  45. return implode('', $chars);
  46. }
  47. public function __construct(array $salt = array()) {
  48. global $config;
  49. if (!empty($salt)) {
  50. // create a salted hash of the "extra salt"
  51. $this->salt = implode(':', $salt);
  52. } else {
  53. $this->salt = '';
  54. }
  55. shuffle($config['spam']['hidden_input_names']);
  56. $input_count = mt_rand($config['spam']['hidden_inputs_min'], $config['spam']['hidden_inputs_max']);
  57. $hidden_input_names_x = 0;
  58. for ($x = 0; $x < $input_count ; $x++) {
  59. if ($hidden_input_names_x === false || mt_rand(0, 2) == 0) {
  60. // Use an obscure name
  61. $name = $this->randomString(mt_rand(10, 40), false, false, $config['spam']['unicode']);
  62. } else {
  63. // Use a pre-defined confusing name
  64. $name = $config['spam']['hidden_input_names'][$hidden_input_names_x++];
  65. if ($hidden_input_names_x >= count($config['spam']['hidden_input_names']))
  66. $hidden_input_names_x = false;
  67. }
  68. if (mt_rand(0, 2) == 0) {
  69. // Value must be null
  70. $this->inputs[$name] = '';
  71. } elseif (mt_rand(0, 4) == 0) {
  72. // Numeric value
  73. $this->inputs[$name] = (string)mt_rand(0, 100000);
  74. } else {
  75. // Obscure value
  76. $this->inputs[$name] = $this->randomString(mt_rand(5, 100), true, true, $config['spam']['unicode']);
  77. }
  78. }
  79. }
  80. public static function space() {
  81. if (mt_rand(0, 3) != 0)
  82. return ' ';
  83. return str_repeat(' ', mt_rand(1, 3));
  84. }
  85. public function html($count = false) {
  86. global $config;
  87. $elements = array(
  88. '<input type="hidden" name="%name%" value="%value%">',
  89. '<input type="hidden" value="%value%" name="%name%">',
  90. '<input name="%name%" value="%value%" type="hidden">',
  91. '<input value="%value%" name="%name%" type="hidden">',
  92. '<input style="display:none" type="text" name="%name%" value="%value%">',
  93. '<input style="display:none" type="text" value="%value%" name="%name%">',
  94. '<span style="display:none"><input type="text" name="%name%" value="%value%"></span>',
  95. '<div style="display:none"><input type="text" name="%name%" value="%value%"></div>',
  96. '<div style="display:none"><input type="text" name="%name%" value="%value%"></div>',
  97. '<textarea style="display:none" name="%name%">%value%</textarea>',
  98. '<textarea name="%name%" style="display:none">%value%</textarea>'
  99. );
  100. $html = '';
  101. if ($count === false) {
  102. $count = mt_rand(1, abs(count($this->inputs) / 15) + 1);
  103. }
  104. if ($count === true) {
  105. // all elements
  106. $inputs = array_slice($this->inputs, $this->index);
  107. } else {
  108. $inputs = array_slice($this->inputs, $this->index, $count);
  109. }
  110. $this->index += count($inputs);
  111. foreach ($inputs as $name => $value) {
  112. $element = false;
  113. while (!$element) {
  114. $element = $elements[array_rand($elements)];
  115. $element = str_replace(' ', self::space(), $element);
  116. if (mt_rand(0, 5) == 0)
  117. $element = str_replace('>', self::space() . '>', $element);
  118. if (strpos($element, 'textarea') !== false && $value == '') {
  119. // There have been some issues with mobile web browsers and empty <textarea>'s.
  120. $element = false;
  121. }
  122. }
  123. $element = str_replace('%name%', utf8tohtml($name), $element);
  124. if (mt_rand(0, 2) == 0)
  125. $value = $this->make_confusing($value);
  126. else
  127. $value = utf8tohtml($value);
  128. if (strpos($element, 'textarea') === false)
  129. $value = str_replace('"', '&quot;', $value);
  130. $element = str_replace('%value%', $value, $element);
  131. $html .= $element;
  132. }
  133. return $html;
  134. }
  135. public function reset() {
  136. $this->index = 0;
  137. }
  138. public function hash() {
  139. global $config;
  140. // This is the tricky part: create a hash to validate it after
  141. // First, sort the keys in alphabetical order (A-Z)
  142. $inputs = $this->inputs;
  143. ksort($inputs);
  144. $hash = '';
  145. // Iterate through each input
  146. foreach ($inputs as $name => $value) {
  147. $hash .= $name . '=' . $value;
  148. }
  149. // Add a salt to the hash
  150. $hash .= $config['cookies']['salt'];
  151. // Use SHA1 for the hash
  152. return sha1($hash . $this->salt);
  153. }
  154. }
  155. function _create_antibot($board, $thread) {
  156. global $config, $purged_old_antispam;
  157. $antibot = new AntiBot(array($board, $thread));
  158. if (!isset($purged_old_antispam)) {
  159. $purged_old_antispam = true;
  160. query('DELETE FROM ``antispam`` WHERE `expires` < UNIX_TIMESTAMP()') or error(db_error());
  161. }
  162. if ($thread)
  163. $query = prepare('UPDATE ``antispam`` SET `expires` = UNIX_TIMESTAMP() + :expires WHERE `board` = :board AND `thread` = :thread AND `expires` IS NULL');
  164. else
  165. $query = prepare('UPDATE ``antispam`` SET `expires` = UNIX_TIMESTAMP() + :expires WHERE `board` = :board AND `thread` IS NULL AND `expires` IS NULL');
  166. $query->bindValue(':board', $board);
  167. if ($thread)
  168. $query->bindValue(':thread', $thread);
  169. $query->bindValue(':expires', $config['spam']['hidden_inputs_expire']);
  170. $query->execute() or error(db_error($query));
  171. $query = prepare('INSERT INTO ``antispam`` VALUES (:board, :thread, :hash, UNIX_TIMESTAMP(), NULL, 0)');
  172. $query->bindValue(':board', $board);
  173. $query->bindValue(':thread', $thread);
  174. $query->bindValue(':hash', $antibot->hash());
  175. $query->execute() or error(db_error($query));
  176. return $antibot;
  177. }
  178. function checkSpam(array $extra_salt = array()) {
  179. global $config, $pdo;
  180. if (!isset($_POST['hash']))
  181. return true;
  182. $hash = $_POST['hash'];
  183. if (!empty($extra_salt)) {
  184. // create a salted hash of the "extra salt"
  185. $extra_salt = implode(':', $extra_salt);
  186. } else {
  187. $extra_salt = '';
  188. }
  189. // Reconsturct the $inputs array
  190. $inputs = array();
  191. foreach ($_POST as $name => $value) {
  192. if (in_array($name, $config['spam']['valid_inputs']))
  193. continue;
  194. $inputs[$name] = $value;
  195. }
  196. // Sort the inputs in alphabetical order (A-Z)
  197. ksort($inputs);
  198. $_hash = '';
  199. // Iterate through each input
  200. foreach ($inputs as $name => $value) {
  201. $_hash .= $name . '=' . $value;
  202. }
  203. // Add a salt to the hash
  204. $_hash .= $config['cookies']['salt'];
  205. // Use SHA1 for the hash
  206. $_hash = sha1($_hash . $extra_salt);
  207. if ($hash != $_hash)
  208. return true;
  209. $query = prepare('SELECT `passed` FROM ``antispam`` WHERE `hash` = :hash');
  210. $query->bindValue(':hash', $hash);
  211. $query->execute() or error(db_error($query));
  212. if ((($passed = $query->fetchColumn(0)) === false) || ($passed > $config['spam']['hidden_inputs_max_pass'])) {
  213. // there was no database entry for this hash. most likely expired.
  214. return true;
  215. }
  216. return $hash;
  217. }
  218. function incrementSpamHash($hash) {
  219. $query = prepare('UPDATE ``antispam`` SET `passed` = `passed` + 1 WHERE `hash` = :hash');
  220. $query->bindValue(':hash', $hash);
  221. $query->execute() or error(db_error($query));
  222. }