forked from GithubBackups/vichan
commit
5a80c4ef22
@ -10,19 +10,16 @@ class SecureImageCaptchaQuery {
|
|||||||
private HttpDriver $http;
|
private HttpDriver $http;
|
||||||
private string $domain;
|
private string $domain;
|
||||||
private string $provider_check;
|
private string $provider_check;
|
||||||
private string $extra;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param HttpDriver $http The http client.
|
* @param HttpDriver $http The http client.
|
||||||
* @param string $domain The server's domain.
|
* @param string $domain The server's domain.
|
||||||
* @param string $provider_check Path to the endpoint.
|
* @param string $provider_check Path to the endpoint.
|
||||||
* @param string $extra Extra http parameters.
|
|
||||||
*/
|
*/
|
||||||
function __construct(HttpDriver $http, string $domain, string $provider_check, string $extra) {
|
function __construct(HttpDriver $http, string $domain, string $provider_check) {
|
||||||
$this->http = $http;
|
$this->http = $http;
|
||||||
$this->domain = $domain;
|
$this->domain = $domain;
|
||||||
$this->provider_check = $provider_check;
|
$this->provider_check = $provider_check;
|
||||||
$this->extra = $extra;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,11 +34,12 @@ class SecureImageCaptchaQuery {
|
|||||||
$data = [
|
$data = [
|
||||||
'mode' => 'check',
|
'mode' => 'check',
|
||||||
'text' => $user_text,
|
'text' => $user_text,
|
||||||
'extra' => $this->extra,
|
|
||||||
'cookie' => $user_cookie
|
'cookie' => $user_cookie
|
||||||
];
|
];
|
||||||
|
|
||||||
$ret = $this->http->requestGet($this->domain . '/' . $this->provider_check, $data);
|
$ret = $this->http->requestGet($this->domain . '/' . $this->provider_check, $data);
|
||||||
return $ret === '1';
|
$resp = \json_decode($ret, true, 16, \JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
|
return isset($resp['success']) && $resp['success'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,13 +384,18 @@
|
|||||||
// To enable the native captcha you need to change a couple of settings. Read more at: /inc/captcha/readme.md
|
// To enable the native captcha you need to change a couple of settings. Read more at: /inc/captcha/readme.md
|
||||||
'native' => [
|
'native' => [
|
||||||
// Custom captcha get provider path (if not working get the absolute path aka your url).
|
// Custom captcha get provider path (if not working get the absolute path aka your url).
|
||||||
'provider_get' => '../inc/captcha/entrypoint.php',
|
'provider_get' => 'securimage.php',
|
||||||
// Custom captcha check provider path
|
// Custom captcha check provider path
|
||||||
'provider_check' => '../inc/captcha/entrypoint.php',
|
'provider_check' => 'securimage.php',
|
||||||
// Custom captcha extra field (eg. charset)
|
// Custom captcha extra field (eg. charset)
|
||||||
'extra' => 'abcdefghijklmnopqrstuvwxyz',
|
'extra' => 'abcdefghijklmnopqrstuvwxyz',
|
||||||
// New thread captcha. Require solving a captcha to post a thread.
|
// New thread captcha. Require solving a captcha to post a thread.
|
||||||
'new_thread_capt' => false
|
'new_thread_capt' => false,
|
||||||
|
// Securimage customization options
|
||||||
|
// https://github.com/dapphp/securimage/blob/nextgen/examples/securimage_show_example.php#L49
|
||||||
|
'securimage_options' => ['send_headers' => false, 'no_exit' => true],
|
||||||
|
// Captcha expires (in seconds)
|
||||||
|
'expires_in' => 320
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -86,8 +86,7 @@ function build_context(array $config): Context {
|
|||||||
return new SecureImageCaptchaQuery(
|
return new SecureImageCaptchaQuery(
|
||||||
$c->get(HttpDriver::class),
|
$c->get(HttpDriver::class),
|
||||||
$config['domain'],
|
$config['domain'],
|
||||||
$config['captcha']['native']['provider_check'],
|
$config['captcha']['native']['provider_check']
|
||||||
$config['captcha']['native']['extra']
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
CacheDriver::class => function($c) {
|
CacheDriver::class => function($c) {
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
var tout;
|
var tout;
|
||||||
|
|
||||||
function redo_events(provider, extra) {
|
function redo_events(provider) {
|
||||||
$('.captcha .captcha_text, textarea[id="body"]').off("focus").one("focus", function() { actually_load_captcha(provider, extra); });
|
$('.captcha .captcha_text, textarea[id="body"]').off("focus").one("focus", function() { actually_load_captcha(provider); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function actually_load_captcha(provider, extra) {
|
function actually_load_captcha(provider) {
|
||||||
$('.captcha .captcha_text, textarea[id="body"]').off("focus");
|
$('.captcha .captcha_text, textarea[id="body"]').off("focus");
|
||||||
|
|
||||||
if (tout !== undefined) {
|
if (tout !== undefined) {
|
||||||
clearTimeout(tout);
|
clearTimeout(tout);
|
||||||
}
|
}
|
||||||
|
|
||||||
$.getJSON(provider, {mode: 'get', extra: extra}, function(json) {
|
$.getJSON(provider, {mode: 'get'}, function(json) {
|
||||||
$(".captcha .captcha_cookie").val(json.cookie);
|
$(".captcha .captcha_cookie").val(json.cookie);
|
||||||
$(".captcha .captcha_html").html(json.captchahtml);
|
$(".captcha .captcha_html").html(json.captchahtml);
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
redo_events(provider, extra);
|
redo_events(provider);
|
||||||
}, json.expires_in * 1000);
|
}, json.expires_in * 1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function load_captcha(provider, extra) {
|
function load_captcha(provider) {
|
||||||
$(function() {
|
$(function() {
|
||||||
$(".captcha>td").html("<input class='captcha_text' type='text' name='captcha_text' size='32' maxlength='6' autocomplete='off'>"+
|
$(".captcha>td").html("<input class='captcha_text' type='text' name='captcha_text' size='32' maxlength='6' autocomplete='off'>"+
|
||||||
"<input class='captcha_cookie' name='captcha_cookie' type='hidden'>"+
|
"<input class='captcha_cookie' name='captcha_cookie' type='hidden'>"+
|
||||||
@ -29,15 +29,15 @@ function load_captcha(provider, extra) {
|
|||||||
|
|
||||||
$("#quick-reply .captcha .captcha_text").prop("placeholder", _("Verification"));
|
$("#quick-reply .captcha .captcha_text").prop("placeholder", _("Verification"));
|
||||||
|
|
||||||
$(".captcha .captcha_html").on("click", function() { actually_load_captcha(provider, extra); });
|
$(".captcha .captcha_html").on("click", function() { actually_load_captcha(provider); });
|
||||||
$(document).on("ajax_after_post", function() { actually_load_captcha(provider, extra); });
|
$(document).on("ajax_after_post", function() { actually_load_captcha(provider); });
|
||||||
redo_events(provider, extra);
|
redo_events(provider);
|
||||||
|
|
||||||
$(window).on("quick-reply", function() {
|
$(window).on("quick-reply", function() {
|
||||||
redo_events(provider, extra);
|
redo_events(provider);
|
||||||
$("#quick-reply .captcha .captcha_html").html($("form:not(#quick-reply) .captcha .captcha_html").html());
|
$("#quick-reply .captcha .captcha_html").html($("form:not(#quick-reply) .captcha .captcha_html").html());
|
||||||
$("#quick-reply .captcha .captcha_cookie").val($("form:not(#quick-reply) .captcha .captcha_cookie").html());
|
$("#quick-reply .captcha .captcha_cookie").val($("form:not(#quick-reply) .captcha .captcha_cookie").html());
|
||||||
$("#quick-reply .captcha .captcha_html").on("click", function() { actually_load_captcha(provider, extra); });
|
$("#quick-reply .captcha .captcha_html").on("click", function() { actually_load_captcha(provider); });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
37
post.php
37
post.php
@ -517,12 +517,7 @@ if (isset($_POST['delete'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$query = new SecureImageCaptchaQuery(
|
$query = $context->get(SecureImageCaptchaQuery::class);
|
||||||
$context->get(HttpDriver::class),
|
|
||||||
$config['domain'],
|
|
||||||
$config['captcha']['provider_check'],
|
|
||||||
$config['captcha']['extra']
|
|
||||||
);
|
|
||||||
$success = $query->verify(
|
$success = $query->verify(
|
||||||
$_POST['captcha_text'],
|
$_POST['captcha_text'],
|
||||||
$_POST['captcha_cookie']
|
$_POST['captcha_cookie']
|
||||||
@ -632,22 +627,22 @@ if (isset($_POST['delete'])) {
|
|||||||
$dynamic = $config['captcha']['dynamic'];
|
$dynamic = $config['captcha']['dynamic'];
|
||||||
|
|
||||||
// With our custom captcha provider
|
// With our custom captcha provider
|
||||||
if (($provider === 'native' && !$new_thread_capt)
|
if ($provider === 'native') {
|
||||||
|| ($provider === 'native' && $new_thread_capt && $post['op'])) {
|
if ((!$new_thread_capt && !$post['op']) || ($new_thread_capt && $post['op'])) {
|
||||||
$query = $context->get(SecureImageCaptchaQuery::class);
|
$query = $context->get(SecureImageCaptchaQuery::class);
|
||||||
$success = $query->verify($_POST['captcha_text'], $_POST['captcha_cookie']);
|
$success = $query->verify($_POST['captcha_text'], $_POST['captcha_cookie']);
|
||||||
|
|
||||||
if (!$success) {
|
if (!$success) {
|
||||||
error(
|
error(
|
||||||
"{$config['error']['captcha']}
|
"{$config['error']['captcha']}
|
||||||
<script>
|
<script>
|
||||||
if (actually_load_captcha !== undefined)
|
if (actually_load_captcha !== undefined)
|
||||||
actually_load_captcha(
|
actually_load_captcha(
|
||||||
\"{$config['captcha']['provider_get']}\",
|
\"{$config['captcha']['provider_get']}\"
|
||||||
\"{$config['captcha']['extra']}\"
|
);
|
||||||
);
|
</script>"
|
||||||
</script>"
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remote 3rd party captchas.
|
// Remote 3rd party captchas.
|
||||||
|
127
securimage.php
127
securimage.php
@ -1,73 +1,88 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once('inc/bootstrap.php');
|
require_once('inc/bootstrap.php');
|
||||||
$expires_in = 120;
|
|
||||||
|
|
||||||
function rand_string($length, $charset) {
|
function rand_string(int $length, string $charset): string {
|
||||||
$ret = "";
|
$ret = '';
|
||||||
while ($length--) {
|
while ($length--) {
|
||||||
$ret .= mb_substr($charset, rand(0, mb_strlen($charset, 'utf-8')-1), 1, 'utf-8');
|
$ret .= mb_substr($charset, rand(0, mb_strlen($charset, 'utf-8')-1), 1, 'utf-8');
|
||||||
}
|
}
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup(int $expires_in): void {
|
||||||
global $expires_in;
|
|
||||||
prepare("DELETE FROM `captchas` WHERE `created_at` < ?")->execute([time() - $expires_in]);
|
prepare("DELETE FROM `captchas` WHERE `created_at` < ?")->execute([time() - $expires_in]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleGetRequestCaptcha(array $config): void {
|
||||||
|
$extra = $config['captcha']['native']['extra'];
|
||||||
|
$cookie = rand_string(20, $extra);
|
||||||
|
|
||||||
$mode = @$_GET['mode'];
|
$securimage = new Securimage($config['captcha']['native']['securimage_options']);
|
||||||
switch ($mode) {
|
$securimage->createCode();
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$securimage->show();
|
||||||
|
$rawImage = ob_get_clean();
|
||||||
|
|
||||||
|
$base64Image = 'data:image/png;base64,' . base64_encode($rawImage);
|
||||||
|
$html = '<img src="' . $base64Image . '">';
|
||||||
|
$captchaCode = $securimage->getCode();
|
||||||
|
|
||||||
|
prepare("INSERT INTO `captchas` (`cookie`, `extra`, `text`, `created_at`) VALUES (?, ?, ?, ?)")
|
||||||
|
->execute([$cookie, $extra, $captchaCode->code_display, $captchaCode->creationTime]);
|
||||||
|
|
||||||
|
if (isset($_GET['raw'])) {
|
||||||
|
$_SESSION['captcha_cookie'] = $cookie;
|
||||||
|
header('Content-Type: image/png');
|
||||||
|
echo $rawImage;
|
||||||
|
} else {
|
||||||
|
header("Content-Type: application/json");
|
||||||
|
echo json_encode([
|
||||||
|
"cookie" => $cookie,
|
||||||
|
"captchahtml" => $html,
|
||||||
|
"expires_in" => $config['captcha']['native']['expires_in'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCheckRequestCaptcha(int $expires_in): void {
|
||||||
|
cleanup($expires_in);
|
||||||
|
|
||||||
|
$cookie = $_GET['cookie'] ?? null;
|
||||||
|
$text = $_GET['text'] ?? null;
|
||||||
|
|
||||||
|
if (!$cookie || !$text) {
|
||||||
|
echo json_encode(["success" => false]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = prepare("SELECT * FROM `captchas` WHERE `cookie` = ?");
|
||||||
|
$query->execute([$cookie]);
|
||||||
|
$captchaData = $query->fetchAll();
|
||||||
|
|
||||||
|
if (!$captchaData) {
|
||||||
|
echo json_encode(["success" => false]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare("DELETE FROM `captchas` WHERE `cookie` = ?")->execute([$cookie]);
|
||||||
|
|
||||||
|
$isSuccessful = $captchaData[0]['text'] === $text;
|
||||||
|
echo json_encode(["success" => $isSuccessful]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mode = $_GET['mode'] ?? null;
|
||||||
|
|
||||||
|
switch($mode) {
|
||||||
case 'get':
|
case 'get':
|
||||||
if (!isset ($_GET['extra'])) {
|
handleGetRequestCaptcha($config);
|
||||||
$_GET['extra'] = $config['captcha']['extra'];
|
|
||||||
}
|
|
||||||
|
|
||||||
header("Content-type: application/json");
|
|
||||||
$extra = $_GET['extra'];
|
|
||||||
$cookie = rand_string(20, "abcdefghijklmnopqrstuvwxyz");
|
|
||||||
$i = new Securimage(['send_headers' => false, 'no_exit' => true]);
|
|
||||||
$i->createCode();
|
|
||||||
ob_start();
|
|
||||||
$i->show();
|
|
||||||
$rawimg = ob_get_contents();
|
|
||||||
$b64img = 'data:image/png;base64,'.base64_encode($rawimg);
|
|
||||||
$html = '<img src="'.$b64img.'">';
|
|
||||||
ob_end_clean();
|
|
||||||
$cdata = $i->getCode();
|
|
||||||
$query = prepare("INSERT INTO `captchas` (`cookie`, `extra`, `text`, `created_at`) VALUES (?, ?, ?, ?)");
|
|
||||||
$query->execute([$cookie, $extra, $cdata->code_display, $cdata->creationTime]);
|
|
||||||
if (isset($_GET['raw'])) {
|
|
||||||
$_SESSION['captcha_cookie'] = $cookie;
|
|
||||||
header('Content-Type: image/png');
|
|
||||||
echo $rawimg;
|
|
||||||
} else {
|
|
||||||
echo json_encode(["cookie" => $cookie, "captchahtml" => $html, "expires_in" => $expires_in]);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'check':
|
case 'check':
|
||||||
cleanup();
|
handleCheckRequestCaptcha($config['captcha']['native']['expires_in']);
|
||||||
if (!isset ($_GET['mode']) || !isset ($_GET['cookie']) || !isset ($_GET['extra']) || !isset ($_GET['text'])) {
|
|
||||||
die();
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = prepare("SELECT * FROM `captchas` WHERE `cookie` = ? AND `extra` = ?");
|
|
||||||
$query->execute([$_GET['cookie'], $_GET['extra']]);
|
|
||||||
|
|
||||||
$ary = $query->fetchAll();
|
|
||||||
|
|
||||||
if (!$ary) { // captcha expired
|
|
||||||
echo "0";
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
$query = prepare("DELETE FROM `captchas` WHERE `cookie` = ? AND `extra` = ?");
|
|
||||||
$query->execute([$_GET['cookie'], $_GET['extra']]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ary[0]['text'] !== $_GET['text']) {
|
|
||||||
echo "0";
|
|
||||||
} else {
|
|
||||||
echo "1";
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
case '':
|
||||||
|
default:
|
||||||
|
http_response_code(400);
|
||||||
|
echo json_encode(["success" => false, "error" => "Invalid mode"]);
|
||||||
|
break;
|
||||||
|
}
|
@ -100,38 +100,22 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if config.captcha.provider == 'native' %}
|
{% if (config.captcha.provider == 'native' and not config.captcha.native.new_thread_capt) or
|
||||||
<tr class='captcha'>
|
(config.captcha.provider == 'native' and config.captcha.native.new_thread_capt and not id) %}
|
||||||
<th>
|
<tr class='captcha'>
|
||||||
{% trans %}Verification{% endtrans %}
|
<th>
|
||||||
</th>
|
{% trans %}Verification{% endtrans %}
|
||||||
<td>
|
</th>
|
||||||
<script>load_captcha("{{ config.captcha.native.provider_get }}", "{{ config.captcha.native.extra }}");</script>
|
<td>
|
||||||
<noscript>
|
<script>load_captcha("{{ config.captcha.native.provider_get }}");</script>
|
||||||
<input class='captcha_text' type='text' name='captcha_text' size='32' maxlength='6' autocomplete='off'>
|
<noscript>
|
||||||
<div class="captcha_html">
|
<input class='captcha_text' type='text' name='captcha_text' size='32' maxlength='6' autocomplete='off'>
|
||||||
<img src="/{{ config.captcha.native.provider_get }}?mode=get&raw=1">
|
<div class="captcha_html">
|
||||||
</div>
|
<img src="/{{ config.captcha.native.provider_get }}?mode=get&raw=1">
|
||||||
</noscript>
|
</div>
|
||||||
</td>
|
</noscript>
|
||||||
</tr>
|
</td>
|
||||||
{% elseif config.captcha.native.new_thread_capt %}
|
</tr>
|
||||||
{% if not id %}
|
|
||||||
<tr class='captcha'>
|
|
||||||
<th>
|
|
||||||
{% trans %}Verification{% endtrans %}
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<script>load_captcha("{{ config.captcha.native.provider_get }}", "{{ config.captcha.native.extra }}");</script>
|
|
||||||
<noscript>
|
|
||||||
<input class='captcha_text' type='text' name='captcha_text' size='32' maxlength='6' autocomplete='off'>
|
|
||||||
<div class="captcha_html">
|
|
||||||
<img src="/{{ config.captcha.native.provider_get }}?mode=get&raw=1">
|
|
||||||
</div>
|
|
||||||
</noscript>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if config.user_flag %}
|
{% if config.user_flag %}
|
||||||
<tr>
|
<tr>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user