captcha_gen.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <?php
  2. class captcha{
  3. public function __construct($frontend, $get, $filters, $page, $output){
  4. /*
  5. Validate cookie, if it exists
  6. */
  7. if(isset($_COOKIE["pass"])){
  8. if(
  9. // check if key is not malformed
  10. preg_match(
  11. '/^c[0-9]+\.[A-Za-z0-9]{20}$/',
  12. $_COOKIE["pass"]
  13. ) &&
  14. // does key exist
  15. apcu_exists($_COOKIE["pass"])
  16. ){
  17. // exists, increment counter
  18. $inc = apcu_inc($_COOKIE["pass"]);
  19. // we start counting from 1
  20. // when it has been incremented to 102, it has reached
  21. // 100 reqs
  22. if($inc >= 102){
  23. // reached limit, delete and give captcha
  24. apcu_delete($_COOKIE["pass"]);
  25. }else{
  26. // the cookie is OK! dont die() and give results
  27. if($output === true){
  28. $frontend->loadheader(
  29. $get,
  30. $filters,
  31. $page
  32. );
  33. }
  34. return;
  35. }
  36. }
  37. }
  38. if($output === false){
  39. echo json_encode([
  40. "status" => "The \"pass\" token in your cookies is missing or has expired!!"
  41. ]);
  42. die();
  43. }
  44. /*
  45. Validate form data
  46. */
  47. $lines =
  48. explode(
  49. "\r\n",
  50. file_get_contents("php://input")
  51. );
  52. $invalid = false;
  53. $answers = [];
  54. $key = false;
  55. $error = "";
  56. foreach($lines as $line){
  57. $line = explode("=", $line, 2);
  58. if(count($line) !== 2){
  59. $invalid = true;
  60. break;
  61. }
  62. preg_match(
  63. '/^c\[([0-9]+)\]$/',
  64. $line[0],
  65. $regex
  66. );
  67. if(
  68. $line[1] != "on" ||
  69. !isset($regex[0][1])
  70. ){
  71. // check if its k
  72. if(
  73. $line[0] == "k" &&
  74. strpos($line[1], "c.") === 0
  75. ){
  76. $key = apcu_fetch($line[1]);
  77. apcu_delete($line[1]);
  78. }
  79. break;
  80. }
  81. $regex = (int)$regex[1];
  82. if(
  83. $regex >= 16 ||
  84. $regex <= -1
  85. ){
  86. $invalid = true;
  87. break;
  88. }
  89. $answers[] = $regex;
  90. }
  91. if(
  92. !$invalid &&
  93. $key !== false
  94. ){
  95. $check = $key[1];
  96. // validate answer
  97. for($i=0; $i<count($key[0]); $i++){
  98. if(!in_array($i, $answers)){
  99. continue;
  100. }
  101. if($key[0][$i][0] == $key[2]){
  102. $check--;
  103. }else{
  104. // got a wrong answer
  105. $check = -1;
  106. break;
  107. }
  108. }
  109. if($check === 0){
  110. // we passed the captcha
  111. // set cookie
  112. $inc = apcu_inc("cookie");
  113. $chars =
  114. array_merge(
  115. range("A", "Z"),
  116. range("a", "z"),
  117. range(0, 9)
  118. );
  119. $c = count($chars) - 1;
  120. $key = "c" . $inc . ".";
  121. for($i=0; $i<20; $i++){
  122. $key .= $chars[random_int(0, $c)];
  123. }
  124. apcu_inc($key, 1, $stupid, 86400);
  125. setcookie(
  126. "pass",
  127. $key,
  128. [
  129. "expires" => time() + 86400, // expires in 24 hours
  130. "samesite" => "Strict",
  131. "path" => "/"
  132. ]
  133. );
  134. $frontend->loadheader(
  135. $get,
  136. $filters,
  137. $page
  138. );
  139. return;
  140. }else{
  141. $error = "<div class=\"quote\">You were <a href=\"https://www.youtube.com/watch?v=e1d7fkQx2rk\" target=\"_BLANK\" rel=\"noreferrer nofollow\">kicked out of Mensa.</a> Please try again.</div>";
  142. }
  143. }
  144. /*
  145. Generate random grid data to pass to captcha.php
  146. */
  147. $dataset = [
  148. ["birds", 2263],
  149. ["fumo_plushies", 1006],
  150. ["minecraft", 848]
  151. ];
  152. // get the positions for the answers
  153. // will return between 3 and 6 answer positions
  154. $range = range(0, 15);
  155. $answer_pos = [];
  156. array_splice($range, 0, 1);
  157. for($i=0; $i<random_int(3, 6); $i++){
  158. $answer_pos_tmp =
  159. array_splice(
  160. $range,
  161. random_int(
  162. 0,
  163. 14 - $i
  164. ),
  165. 1
  166. );
  167. $answer_pos[] = $answer_pos_tmp[0];
  168. }
  169. // choose a dataset
  170. $choosen = &$dataset[random_int(0, count($dataset) - 1)];
  171. $choices = [];
  172. for($i=0; $i<count($dataset); $i++){
  173. if($dataset[$i][0] == $choosen[0]){
  174. continue;
  175. }
  176. $choices[] = $dataset[$i];
  177. }
  178. // generate grid data
  179. $grid = [];
  180. for($i=0; $i<16; $i++){
  181. if(in_array($i, $answer_pos)){
  182. $grid[] = $choosen;
  183. }else{
  184. $grid[] = $choices[random_int(0, count($choices) - 1)];
  185. }
  186. }
  187. $key = "c." . apcu_inc("captcha_gen", 1) . "." . random_int(0, 100000000);
  188. apcu_store(
  189. $key,
  190. [
  191. $grid,
  192. count($answer_pos),
  193. $choosen[0],
  194. false // has captcha been generated?
  195. ],
  196. 120 // we give user 2 minutes to get captcha, in case of network error
  197. );
  198. $payload = [
  199. "class" => "",
  200. "right-left" => "",
  201. "right-right" => "",
  202. "left" =>
  203. '<div class="infobox">' .
  204. '<h1>IQ test</h1>' .
  205. 'Due to getting hit with 20,000 bot requests per day, I had to put this up. Sorry.<br><br>' .
  206. 'Solving this captcha will allow you to make 100 searches today. I will add a way for legit users to bypass the captcha later. Sorry /g/tards!!' .
  207. $error .
  208. '<form method="POST" enctype="text/plain" autocomplete="off">' .
  209. '<div class="captcha-wrapper">' .
  210. '<div class="captcha">' .
  211. '<img src="captcha.php?k=' . $key . '" alt="Captcha image">' .
  212. '<div class="captcha-controls">' .
  213. '<input type="checkbox" name="c[0]" id="c0">' .
  214. '<label for="c0"></label>' .
  215. '<input type="checkbox" name="c[1]" id="c1">' .
  216. '<label for="c1"></label>' .
  217. '<input type="checkbox" name="c[2]" id="c2">' .
  218. '<label for="c2"></label>' .
  219. '<input type="checkbox" name="c[3]" id="c3">' .
  220. '<label for="c3"></label>' .
  221. '<input type="checkbox" name="c[4]" id="c4">' .
  222. '<label for="c4"></label>' .
  223. '<input type="checkbox" name="c[5]" id="c5">' .
  224. '<label for="c5"></label>' .
  225. '<input type="checkbox" name="c[6]" id="c6">' .
  226. '<label for="c6"></label>' .
  227. '<input type="checkbox" name="c[7]" id="c7">' .
  228. '<label for="c7"></label>' .
  229. '<input type="checkbox" name="c[8]" id="c8">' .
  230. '<label for="c8"></label>' .
  231. '<input type="checkbox" name="c[9]" id="c9">' .
  232. '<label for="c9"></label>' .
  233. '<input type="checkbox" name="c[10]" id="c10">' .
  234. '<label for="c10"></label>' .
  235. '<input type="checkbox" name="c[11]" id="c11">' .
  236. '<label for="c11"></label>' .
  237. '<input type="checkbox" name="c[12]" id="c12">' .
  238. '<label for="c12"></label>' .
  239. '<input type="checkbox" name="c[13]" id="c13">' .
  240. '<label for="c13"></label>' .
  241. '<input type="checkbox" name="c[14]" id="c14">' .
  242. '<label for="c14"></label>' .
  243. '<input type="checkbox" name="c[15]" id="c15">' .
  244. '<label for="c15"></label>' .
  245. '</div>' .
  246. '</div>' .
  247. '</div>' .
  248. '<input type="hidden" name="k" value="' . $key . '">' .
  249. '<input type="submit" value="Check IQ" class="captcha-submit">' .
  250. '</form>' .
  251. '</div>'
  252. ];
  253. http_response_code(429); // too many reqs
  254. $frontend->loadheader(
  255. $get,
  256. $filters,
  257. "web"
  258. );
  259. echo $frontend->load("search.html", $payload);
  260. die();
  261. }
  262. }