summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorilotterytea <iltsu@alright.party>2025-05-15 15:20:53 +0500
committerilotterytea <iltsu@alright.party>2025-05-15 15:20:53 +0500
commit20ae2ce5e02539719b971e53222f3e3328ff82a6 (patch)
tree1ff0014bf73d73c2f42819a45d50d3a04c53d680
parenta3522672930578959980e39b7041b120c13cd6cf (diff)
feat: custom captcha
-rw-r--r--.gitignore3
-rw-r--r--public/captcha.php92
-rw-r--r--public/emotes/upload.php12
-rw-r--r--src/accounts.php5
-rw-r--r--src/captcha.php151
-rw-r--r--src/config.sample.php5
6 files changed, 211 insertions, 57 deletions
diff --git a/.gitignore b/.gitignore
index 665c2a6..6d1f0c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
userdata/
*.db
config.php
-custom_static/ \ No newline at end of file
+custom_static/
+captcha/ \ No newline at end of file
diff --git a/public/captcha.php b/public/captcha.php
index 58283bf..b454b7d 100644
--- a/public/captcha.php
+++ b/public/captcha.php
@@ -1,65 +1,59 @@
<?php
include_once "../src/config.php";
include_once "../src/alert.php";
+include_once "../src/captcha.php";
+include_once "../src/utils.php";
session_start();
-if (!HCAPTCHA_ENABLE) {
- $_SESSION["captcha_solved"] = true;
- header("Location: /");
+if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST["answer"])) {
+ if ($_POST["answer"] == ($_SESSION["captcha_word"] ?? "")) {
+ $_SESSION["captcha_solved"] = true;
+ echo json_response([
+ "status_code" => 200,
+ "message" => "Solved!",
+ "data" => null
+ ]);
+ } else {
+ echo json_response([
+ "status_code" => 400,
+ "message" => "Wrong answer!",
+ "data" => null
+ ], 400);
+ }
exit;
}
-if (isset($_SESSION["captcha_solved"]) && $_SESSION["captcha_solved"]) {
- header("Location: /");
+$file_folder = $_SERVER["DOCUMENT_ROOT"] . '/static/img/captcha';
+
+if (!CAPTCHA_ENABLE || ($_SESSION["captcha_solved"] ?? false) || !is_dir($file_folder)) {
+ $_SESSION["captcha_solved"] = true;
+ echo json_response([
+ "status_code" => 200,
+ "message" => "No need to solve captcha",
+ "data" => null
+ ]);
exit;
}
-if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST["h-captcha-response"])) {
- // sending a request to captcha api
- $request = curl_init("https://hcaptcha.com/siteverify");
- curl_setopt($request, CURLOPT_POST, 1);
- curl_setopt($request, CURLOPT_HTTPHEADER, [sprintf("User-Agent: %s/1.0", INSTANCE_NAME)]);
- curl_setopt(
- $request,
- CURLOPT_POSTFIELDS,
- http_build_query(array("secret" => HCAPTCHA_SECRETKEY, "response" => $_POST["h-captcha-response"]))
- );
- curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
-
- $response = curl_exec($request);
- curl_close($request);
-
- $json = json_decode($response);
-
- if ($json->success) {
- $_SESSION["captcha_solved"] = true;
- header("Location: /");
- exit;
- }
-}
-?>
+$files = scandir($file_folder);
+array_splice($files, 0, 2);
-<html>
+$filename = $files[random_int(0, count($files) - 1)];
+$filename = basename($filename, ".png");
-<head>
- <title>Resolving a hCaptcha - <?php echo INSTANCE_NAME ?></title>
- <link rel="stylesheet" href="/static/style.css">
- <link rel="shortcut icon" href="/static/favicon.ico" type="image/x-icon">
- <script src='https://www.hCaptcha.com/1/api.js' async defer></script>
-</head>
+$_SESSION["captcha_word"] = $filename;
-<body>
- <noscript>JavaScript is required to solve hCaptcha</noscript>
- <div class="container">
- <div class="wrapper">
- <section class="row" style="padding: 4px; justify-content: center;">
- <section class="box">
- <div class="h-captcha" data-sitekey="<?php echo HCAPTCHA_SITEKEY ?>"></div>
- </section>
- </section>
- </div>
- </div>
-</body>
+$image = generate_image_captcha(
+ CAPTCHA_SIZE[0],
+ CAPTCHA_SIZE[1],
+ random_int(1, 3),
+ $filename,
+ $file_folder
+);
-</html> \ No newline at end of file
+echo json_response([
+ "status_code" => 200,
+ "message" => null,
+ "data" => $image
+]); \ No newline at end of file
diff --git a/public/emotes/upload.php b/public/emotes/upload.php
index e4ff6cc..4506152 100644
--- a/public/emotes/upload.php
+++ b/public/emotes/upload.php
@@ -2,6 +2,7 @@
include "../../src/accounts.php";
include_once "../../src/config.php";
include_once "../../src/alert.php";
+include_once "../../src/captcha.php";
if (!EMOTE_UPLOAD) {
generate_alert("/404.php", "Emote upload is disabled", 403);
@@ -135,6 +136,12 @@ if ($_SERVER['REQUEST_METHOD'] != "POST") {
</div>
</section>
+ <?php
+ if (CAPTCHA_ENABLE && (CAPTCHA_FORCE_USERS || !isset($_SESSION["user_id"]))) {
+ html_captcha_form();
+ }
+ ?>
+
<section class="box column" id="emote-showcase" style="display: none;">
<div class="emote-showcase">
<div class="emote-image">
@@ -319,6 +326,11 @@ if ($_SERVER['REQUEST_METHOD'] != "POST") {
exit;
}
+if (!CLIENT_REQUIRES_JSON && CAPTCHA_ENABLE && !isset($_SESSION["captcha_solved"])) {
+ generate_alert("/404.php", "You haven't solved captcha yet.", 403);
+ exit;
+}
+
$is_manual = intval($_POST["manual"] ?? "0") == 1;
if ($is_manual && !isset($_FILES["file-1x"], $_FILES["file-2x"], $_FILES["file-3x"])) {
diff --git a/src/accounts.php b/src/accounts.php
index 99d1a9c..72c766f 100644
--- a/src/accounts.php
+++ b/src/accounts.php
@@ -5,11 +5,6 @@ function authorize_user(bool $required = false): bool
{
session_start();
- if (!isset($_SESSION["captcha_solved"]) && !CLIENT_REQUIRES_JSON) {
- header("Location: /captcha.php");
- exit;
- }
-
if (!isset($_COOKIE["secret_key"]) && !isset($_SERVER["HTTP_AUTHORIZATION"])) {
if (isset($_SESSION["user_id"])) {
session_unset();
diff --git a/src/captcha.php b/src/captcha.php
new file mode 100644
index 0000000..d6d2547
--- /dev/null
+++ b/src/captcha.php
@@ -0,0 +1,151 @@
+<?php
+function generate_image_captcha(int $width, int $height, int $difficulty, string $file_name, string $file_folder): string
+{
+ $image = imagecreatetruecolor($width, $height);
+
+ $background = imagecolorallocate($image, 0xDD, 0xDD, 0xDD);
+ imagefilledrectangle($image, 0, 0, $width, $height, $background);
+
+ $files = scandir($file_folder);
+ array_splice($files, 0, 2);
+
+ for ($i = 0; $i < 50 * $difficulty; $i++) {
+ $unprocessed = imagecreatefrompng("$file_folder/" . $files[random_int(0, count($files) - 1)]);
+
+ $oldw = imagesx($unprocessed);
+ $oldh = imagesy($unprocessed);
+
+ $w = random_int(round($oldw / 4), round($oldw / 2));
+ $h = random_int(round($oldh / 4), round($oldh / 2));
+
+ $file = imagecreatetruecolor($w, $h);
+ imagealphablending($file, false);
+ $transparent = imagecolorallocatealpha($file, 0, 0, 0, 127);
+ imagefill($file, 0, 0, $transparent);
+ imagesavealpha($file, true);
+
+ imagecopyresampled($file, $unprocessed, 0, 0, 0, 0, $w, $h, $oldw, $oldh);
+
+ $angle = random_int(0, 360);
+
+ $file = imagerotate($file, $angle, $transparent);
+
+ for ($j = 0; $j < random_int(2, 5 * $difficulty); $j++) {
+ imagefilter($file, IMG_FILTER_GAUSSIAN_BLUR);
+ }
+
+ if (random_int(0, 15) % 3 == 0) {
+ imagefilter($file, IMG_FILTER_NEGATE);
+ }
+
+ if (random_int(0, 20) % 4 == 0) {
+ imagefilter($file, IMG_FILTER_PIXELATE, 4);
+ }
+
+ $w = imagesx($file);
+ $h = imagesy($file);
+
+ imagecopy(
+ $image,
+ $file,
+ random_int(0, $width - $w),
+ random_int(0, $height - $h),
+ 0,
+ 0,
+ $w,
+ $h
+ );
+ }
+
+ $foreground = imagecreatefrompng("$file_folder/$file_name.png");
+ $transparent = imagecolorallocatealpha($foreground, 0, 0, 0, 127);
+ $angle = random_int(0, max: 180);
+ $foreground = imagerotate($foreground, $angle, $transparent);
+ $w = imagesx($foreground);
+ $h = imagesy($foreground);
+ imagecopy(
+ $image,
+ $foreground,
+ random_int(0, $width - $w),
+ random_int(0, $height - $h),
+ 0,
+ 0,
+ $w,
+ $h
+ );
+
+ ob_start();
+ imagepng($image);
+ $source = ob_get_contents();
+ ob_clean();
+
+ return "data:image/png;base64," . base64_encode($source);
+}
+
+function html_captcha_form()
+{
+ echo '' ?>
+ <div class="box" id="form-captcha-wrapper" style="display: none;">
+ <div class="box navtab">
+ Solve captcha
+ </div>
+ <div class="box content">
+ <noscript>JavaScript is required for captcha!</noscript>
+ <form id="form-captcha">
+ <img src="" alt="Generating captcha..." id="form-captcha-img" width="256">
+ <div class="column small-gap">
+ <div class="row small-gap">
+ <input type="text" name="answer" placeholder="Enter emote name..." class="grow"
+ id="form-captcha-answer">
+ <button type="submit" class="green" id="form-captcha-solve">Solve</button>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ <script>
+ const formElement = document.getElementById("form-captcha");
+ const formWrapper = document.getElementById("form-captcha-wrapper");
+
+ function get_captcha() {
+ fetch("/captcha.php")
+ .then((response) => response.json())
+ .then((json) => {
+ if (json.data == null) {
+ formWrapper.style.display = "none";
+ return;
+ }
+
+ document.getElementById("form-captcha-answer").value = null;
+
+ formWrapper.style.display = "flex";
+
+ document.getElementById("form-captcha-img").setAttribute("src", json.data);
+ });
+ }
+
+ get_captcha();
+
+ formElement.addEventListener("submit", (e) => {
+ e.preventDefault();
+
+ const answer = document.getElementById("form-captcha-answer");
+ const body = new FormData(formElement);
+
+ fetch("/captcha.php", {
+ "method": "POST",
+ "body": body
+ })
+ .then((response) => response.json())
+ .then((json) => {
+ if (json.status_code == 200 && json.data == null) {
+ formWrapper.style.display = "none";
+ return;
+ }
+
+ get_captcha();
+ });
+ });
+ </script>
+ <?php ;
+} \ No newline at end of file
diff --git a/src/config.sample.php b/src/config.sample.php
index ed206f6..3d30044 100644
--- a/src/config.sample.php
+++ b/src/config.sample.php
@@ -65,9 +65,10 @@ define("TWITCH_CLIENT_ID", "AAAAAAAAA"); // Client ID of your Twitch application
define("TWITCH_SECRET_KEY", "BBBBBBBBB"); // Secret key of your Twitch application.
define("TWITCH_REDIRECT_URI", ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http") . "://$_SERVER[HTTP_HOST]/account/login/twitch.php"); // Redirect URI of your Twitch application.
-// HCAPTCHA
+// CAPTCHA
define("CAPTCHA_ENABLE", true); // Enable built-in captcha.
-define("CAPTCHA_WORDS", ["hello", "apple", "cat"]); // Captcha words.
+define("CAPTCHA_SIZE", [580, 220]); // Captcha size.
+define("CAPTCHA_FORCE_USERS", false); // Force authorized users to solve captcha.
// FOR DEVELOPERS
define("CLIENT_REQUIRES_JSON", isset($_SERVER["HTTP_ACCEPT"]) && $_SERVER["HTTP_ACCEPT"] == "application/json"); \ No newline at end of file