Friendly, easy, lightweight, self-hostable CAPTCHA service. https://captcheck.netsyms.com
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.

138 lines
6.5 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. <?php
  2. require __DIR__ . '/required.php';
  3. header("Content-Type: application/json");
  4. // Oldest session allowed
  5. $session_min_date = date("Y-m-d H:i:s", strtotime("-" . SESSION_EXPIRE_MINUTES . " minutes"));
  6. // Delete old sessions
  7. $old_sessions = $database->select("sessions", "sid", ["timestamp[<]" => $session_min_date]);
  8. $database->delete("scrambled_answers", ["sid" => $old_sessions]);
  9. $database->delete("sessions", ["sid" => $old_sessions]);
  10. switch ($VARS['action']) {
  11. case "ping":
  12. $out = ["status" => "OK", "pong" => true];
  13. exit(json_encode($out));
  14. case "new":
  15. // generate unique session ID that has an essentially zero chance of being a duplicate.
  16. // Contains a hash of a secure random number, a hash of the user's IP, and 23 uniqid() characters.
  17. $skey = uniqid(substr(hash("md5", mt_rand()), 3, 5) . hash("md5", getUserIP()), true);
  18. // Image problem
  19. //
  20. // Get five random options
  21. $answer_count = $database->count('answers');
  22. $answers = $database->select('answers', ['aid', 'aname'], ["LIMIT" => [mt_rand(0, $answer_count - 6), 5]]);
  23. shuffle($answers);
  24. // Pick a correct one at random
  25. $correct_answer = $answers[mt_rand(0, count($answers) - 1)];
  26. // Scramble the answer names so the client doesn't know the real answers.
  27. $scrambled = ["real" => [], "fake" => []];
  28. foreach ($answers as $a) {
  29. $scrambled["real"][] = $a['aid'];
  30. $scrambled["fake"][] = substr(hash("md5", mt_rand()), 0, 20);
  31. }
  32. // Text problem
  33. //
  34. // Get random question
  35. $access_count = $database->count('access_questions');
  36. $access_question = $database->select('access_questions', ['acqid', 'acqtext'], ["LIMIT" => [mt_rand(0, $access_count - 1), 1]])[0];
  37. // Save the session data
  38. $database->insert("sessions", ["skey" => $skey, "aid" => $correct_answer['aid'], "acqid" => $access_question['acqid'], "expired" => 0, "#timestamp" => "NOW()", "ipaddr" => getUserIP()]);
  39. $sid = $database->id();
  40. // Save the answer data
  41. $scrambled_insert = [];
  42. for ($i = 0; $i < count($scrambled['real']); $i++) {
  43. $scrambled_insert[] = ["sid" => $sid, "aid" => $scrambled['real'][$i], "acode" => $scrambled['fake'][$i]];
  44. }
  45. $database->insert("scrambled_answers", $scrambled_insert);
  46. // Vary question wording a little
  47. $questions = ["Please click on the [].", "Click the [].", "Find the []."];
  48. shuffle($questions);
  49. $resp = [
  50. "session" => $skey,
  51. "id_prefix" => substr(hash("md5", mt_rand()), 3, 5),
  52. "question_i" => str_replace("[]", $correct_answer['aname'], $questions[0]),
  53. "question_a" => $access_question['acqtext'],
  54. "answers" => $scrambled["fake"]
  55. ];
  56. exit(json_encode($resp));
  57. case "img":
  58. if (!$database->has('sessions', ['skey' => $VARS['s']])) {
  59. sendError("Missing or invalid session ID.", "client");
  60. }
  61. $sid = $database->get('sessions', 'sid', ['skey' => $VARS['s']]);
  62. if (!$database->has("scrambled_answers", ["AND" => ["sid" => $sid, "acode" => $VARS['c']]])) {
  63. sendError("Missing or invalid image code.", "client");
  64. }
  65. $imgid = $database->get("scrambled_answers", ["[>]answers" => ["aid" => "aid"]], 'aimg', ["AND" => ["sid" => $sid, "acode" => $VARS['c']]]);
  66. /* Load image, add some black/white noise, and send */
  67. header('Content-Type: image/png');
  68. $imgpath = __DIR__ . "/images/" . $imgid . ".png";
  69. if (DEBUG) {
  70. file_put_contents("debug", $imgpath . "\n", FILE_APPEND);
  71. }
  72. $img = imagecreatefrompng($imgpath);
  73. imageAlphaBlending($img, true);
  74. imageSaveAlpha($img, true);
  75. $black = imagecolorallocate($img, 0, 0, 0);
  76. $white = imagecolorallocate($img, 255, 255, 255);
  77. // Add static noise
  78. for ($i = 0; $i < 150; $i++) {
  79. imagesetpixel($img, mt_rand(0, 63), mt_rand(0, 63), $black);
  80. }
  81. for ($i = 0; $i < 75; $i++) {
  82. imagesetpixel($img, mt_rand(0, 63), mt_rand(0, 63), $white);
  83. }
  84. // Add lines
  85. for ($i = 0; $i < 2; $i++) {
  86. imageline($img, mt_rand(0, 63), mt_rand(0, 63), mt_rand(0, 63), mt_rand(0, 63), $black);
  87. }
  88. for ($i = 0; $i < 5; $i++) {
  89. imageline($img, mt_rand(0, 63), mt_rand(0, 63), mt_rand(0, 63), mt_rand(0, 63), $white);
  90. }
  91. imagepng($img);
  92. exit();
  93. case "verify":
  94. if (!$database->has('sessions', ['skey' => $VARS['session_id']])) {
  95. echo json_encode(["session" => $VARS['session_id'], "result" => false, "msg" => "Session invalid."]);
  96. exit();
  97. }
  98. $sid = $database->get('sessions', 'sid', ['skey' => $VARS['session_id']]);
  99. $expired = ($database->get('sessions', 'expired', ['skey' => $VARS['session_id']]) == 1 ? true : false);
  100. if ($expired) {
  101. echo json_encode(["session" => $VARS['session_id'], "result" => false, "msg" => "Session key already used."]);
  102. exit();
  103. }
  104. $image = false;
  105. if ($database->has("scrambled_answers", ["AND" => ["sid" => $sid, "acode" => $VARS['answer_id']]])) {
  106. // Image maybe correct
  107. $image = true;
  108. } else if ($database->has("sessions", ["[>]access_answers" => ["acqid" => "acqid"]], ["AND" => ["sid" => $sid, "OR" => ["acatext" => strtolower($VARS['answer_id']), "acahash" => hash('md5', strtolower($VARS['answer_id']))]]])) {
  109. // Accessible text correct
  110. $image = false;
  111. } else {
  112. // Invalid answer
  113. echo json_encode(["session" => $VARS['session_id'], "result" => false, "msg" => "Answer invalid."]);
  114. exit();
  115. }
  116. if ($image) {
  117. $aid = $database->get('scrambled_answers', 'aid', ["AND" => ["sid" => $sid, "acode" => $VARS['answer_id']]]);
  118. if ($database->has('sessions', ["AND" => ["sid" => $sid, "aid" => $aid]])) {
  119. echo json_encode(["session" => $VARS['session_id'], "result" => true]);
  120. } else {
  121. echo json_encode(["session" => $VARS['session_id'], "result" => false, "msg" => "Answer incorrect."]);
  122. }
  123. } else {
  124. echo json_encode(["session" => $VARS['session_id'], "result" => true]);
  125. }
  126. $database->update("sessions", ['expired' => 1], ["sid" => $sid]);
  127. exit();
  128. default:
  129. sendError("Bad Request", "client");
  130. }