forked from leftypol/leftypol
Merge branch 'captcha-workaround' into 'config'
Captcha workaround See merge request leftypol/leftypol!9
This commit is contained in:
commit
aa8525aa86
4 changed files with 121 additions and 82 deletions
|
@ -301,9 +301,8 @@
|
|||
'lock',
|
||||
'raw',
|
||||
'embed',
|
||||
'g-recaptcha-response',
|
||||
'h-captcha-response',
|
||||
'cf-turnstile-response',
|
||||
'captcha-response',
|
||||
'captcha-form-id',
|
||||
'spoiler',
|
||||
'page',
|
||||
'file_url',
|
||||
|
@ -330,33 +329,40 @@
|
|||
'answer' => '4'
|
||||
);
|
||||
*/
|
||||
/**
|
||||
* The captcha is dynamically injected on the client if the server replies with the `captcha-required` cookie set
|
||||
* to 1.
|
||||
* Use false to disable this configuration, otherwise, set the IP that vichan should check for captcha responses.
|
||||
*/
|
||||
$config['dynamic_captcha'] = false;
|
||||
|
||||
// Enable reCaptcha to make spam even harder. Rarely necessary.
|
||||
$config['recaptcha'] = false;
|
||||
|
||||
// Public and private key pair from https://www.google.com/recaptcha/admin/create
|
||||
$config['recaptcha_public'] = '6LcXTcUSAAAAAKBxyFWIt2SO8jwx4W7wcSMRoN3f';
|
||||
$config['recaptcha_private'] = '6LcXTcUSAAAAAOGVbVdhmEM1_SyRF4xTKe8jbzf_';
|
||||
|
||||
// Enable hCaptcha.
|
||||
$config['hcaptcha'] = false;
|
||||
|
||||
// Public and private key pair for using hCaptcha.
|
||||
$config['hcaptcha_public'] = '7a4b21e0-dc53-46f2-a9f8-91d2e74b63a0';
|
||||
$config['hcaptcha_private'] = '0x4e9A01bE637b51dC41a7Ea9865C3fDe4aB72Cf17';
|
||||
|
||||
// Enable Cloudflare's Turnstile captcha.
|
||||
$config['turnstile'] = false;
|
||||
|
||||
// Public and private key pair for turnstile.
|
||||
$config['turnstile_public'] = '';
|
||||
$config['turnstile_private'] = '';
|
||||
// Enable a captcha system to make spam even harder. Rarely necessary.
|
||||
$config['captcha'] = [
|
||||
/**
|
||||
* Select the captcha backend, false to disable.
|
||||
* Can be false, "recaptcha", "hcaptcha" or "turnstile".
|
||||
*/
|
||||
'mode' => false,
|
||||
/**
|
||||
* The captcha is dynamically injected on the client if the server replies with the `captcha-required` cookie set
|
||||
* to 1.
|
||||
* Use false to disable this configuration, otherwise, set the IP that vichan should check for captcha responses.
|
||||
*/
|
||||
'dynamic' => false,
|
||||
// Require to be non-zero if you use js/ajax.js (preferably no more than a few seconds), otherwise weird errors might occur.
|
||||
'passthrough_timeout' => 0,
|
||||
// Configure Google reCAPTCHA.
|
||||
'recaptcha' => [
|
||||
// Public and private key pair from https://www.google.com/recaptcha/admin/create
|
||||
'public' => '6LcXTcUSAAAAAKBxyFWIt2SO8jwx4W7wcSMRoN3f',
|
||||
'private' => '6LcXTcUSAAAAAOGVbVdhmEM1_SyRF4xTKe8jbzf_',
|
||||
],
|
||||
// Configure hCaptcha.
|
||||
'hcaptcha' => [
|
||||
// Public and private key pair for using hCaptcha.
|
||||
'public' => '7a4b21e0-dc53-46f2-a9f8-91d2e74b63a0',
|
||||
'private' => '0x4e9A01bE637b51dC41a7Ea9865C3fDe4aB72Cf17',
|
||||
],
|
||||
// Configure Cloudflare Turnstile.
|
||||
'turnstile' => [
|
||||
// Public and private key pair for turnstile.
|
||||
'public' => '',
|
||||
'private' => '',
|
||||
]
|
||||
];
|
||||
|
||||
// Ability to lock a board for normal users and still allow mods to post. Could also be useful for making an archive board
|
||||
$config['board_locked'] = false;
|
||||
|
|
120
post.php
120
post.php
|
@ -167,6 +167,68 @@ function check_turnstile($secret, $response, $remote_ip, $expected_action)
|
|||
return $json_ret['success'] === true && $json_ret['action'] === $expected_action;
|
||||
}
|
||||
|
||||
/**
|
||||
* A "sophisticated" workaround to js/ajax.js calling post.php multiple times on error/ban.
|
||||
*/
|
||||
function check_captcha(array $captcha_config, string $form_id, string $board_uri, string $response, string $remote_ip, string $expected_action) {
|
||||
$dynamic = $captcha_config['dynamic'];
|
||||
if ($dynamic !== false && $remote_ip !== $dynamic) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch ($captcha_config['mode']) {
|
||||
case 'recaptcha':
|
||||
case 'hcaptcha':
|
||||
case 'turnstile':
|
||||
$mode = $captcha_config['mode'];
|
||||
break;
|
||||
case false:
|
||||
return true;
|
||||
default:
|
||||
\error_log("Unknown captcha mode '{$captcha_config['mode']}'");
|
||||
throw new \RuntimeException('Captcha configuration error');
|
||||
}
|
||||
|
||||
$passthrough_timeout = $captcha_config['passthrough_timeout'];
|
||||
|
||||
if ($passthrough_timeout != 0) {
|
||||
$pass = Cache::get("captcha_passthrough_{$remote_ip}_{$form_id}");
|
||||
if ($pass !== false) {
|
||||
$let_through = $pass['expires'] > time() && $pass['board_uri'] === $board_uri && $pass['captcha_response'] === $response;
|
||||
if ($let_through) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$remote_ip_send = $dynamic !== false ? null : $remote_ip;
|
||||
$private_key = $captcha_config[$mode]['private'];
|
||||
$public_key = $captcha_config[$mode]['public'];
|
||||
switch ($mode) {
|
||||
case 'recaptcha':
|
||||
$ret = check_recaptcha($private_key, $response, $remote_ip_send);
|
||||
break;
|
||||
case 'hcaptcha':
|
||||
$ret = check_hcaptcha($private_key, $response, $remote_ip_send, $public_key);
|
||||
break;
|
||||
case 'turnstile':
|
||||
$ret = check_turnstile($private_key, $response, $remote_ip_send, $expected_action);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($ret && $passthrough_timeout != 0) {
|
||||
$pass = [
|
||||
'expires' => time() + $passthrough_timeout,
|
||||
'board_uri' => $board_uri,
|
||||
'captcha_response' => $response
|
||||
];
|
||||
|
||||
Cache::set("captcha_passthrough_{$remote_ip}_{$form_id}", $pass, $passthrough_timeout + 2);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the (single) captcha associated with the ip and code.
|
||||
*
|
||||
|
@ -765,56 +827,16 @@ function handle_post()
|
|||
|
||||
|
||||
if (!$dropped_post) {
|
||||
if ($config['dynamic_captcha'] !== false) {
|
||||
if ($_SERVER['REMOTE_ADDR'] === $config['dynamic_captcha']) {
|
||||
if ($config['recaptcha']) {
|
||||
if (!isset($_POST['g-recaptcha-response'])) {
|
||||
error($config['error']['bot']);
|
||||
}
|
||||
if (!check_recaptcha($config['recaptcha_private'], $_POST['g-recaptcha-response'], null)) {
|
||||
error($config['error']['captcha']);
|
||||
}
|
||||
} elseif ($config['hcaptcha']) {
|
||||
if (!isset($_POST['h-captcha-response'])) {
|
||||
error($config['error']['bot']);
|
||||
}
|
||||
if (!check_hcaptcha($config['hcaptcha_private'], $_POST['h-captcha-response'], null, $config['hcaptcha_public'])) {
|
||||
error($config['error']['captcha']);
|
||||
}
|
||||
} elseif ($config['turnstile']) {
|
||||
if (!isset($_POST['cf-turnstile-response'])) {
|
||||
error($config['error']['bot']);
|
||||
}
|
||||
$expected_action = $post['op'] ? 'post-thread' : 'post-reply';
|
||||
if (!check_turnstile($config['turnstile_private'], $_POST['cf-turnstile-response'], null, $expected_action)) {
|
||||
error($config['error']['captcha']);
|
||||
}
|
||||
}
|
||||
// Check for CAPTCHA right after opening the board so the "return" link is in there.
|
||||
if ($config['captcha']['mode'] !== false) {
|
||||
if (!isset($_POST['captcha-response'], $_POST['captcha-form-id'])) {
|
||||
error($config['error']['bot']);
|
||||
}
|
||||
} else {
|
||||
// Check for CAPTCHA right after opening the board so the "return" link is in there.
|
||||
if ($config['recaptcha']) {
|
||||
if (!isset($_POST['g-recaptcha-response'])) {
|
||||
error($config['error']['bot']);
|
||||
}
|
||||
if (!check_recaptcha($config['recaptcha_private'], $_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR'])) {
|
||||
error($config['error']['captcha']);
|
||||
}
|
||||
} elseif ($config['hcaptcha']) {
|
||||
if (!isset($_POST['h-captcha-response'])) {
|
||||
error($config['error']['bot']);
|
||||
}
|
||||
if (!check_hcaptcha($config['hcaptcha_private'], $_POST['h-captcha-response'], $_SERVER['REMOTE_ADDR'], $config['hcaptcha_public'])) {
|
||||
error($config['error']['captcha']);
|
||||
}
|
||||
} elseif ($config['turnstile']) {
|
||||
if (!isset($_POST['cf-turnstile-response'])) {
|
||||
error($config['error']['bot']);
|
||||
}
|
||||
$expected_action = $post['op'] ? 'post-thread' : 'post-reply';
|
||||
if (!check_turnstile($config['turnstile_private'], $_POST['cf-turnstile-response'], null, $expected_action)) {
|
||||
error($config['error']['captcha']);
|
||||
}
|
||||
|
||||
$expected_action = $post['op'] ? 'post-thread' : 'post-reply';
|
||||
$ret = check_captcha($config['captcha'], $_POST['captcha-form-id'], $post['board'], $_POST['captcha-response'], $_SERVER['REMOTE_ADDR'], $expected_action);
|
||||
if (!$ret) {
|
||||
error($config['error']['captcha']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -273,8 +273,9 @@ var captcha_renderer = null;
|
|||
function onCaptchaLoadHcaptcha() {
|
||||
if (captcha_renderer === null && (active_page === 'index' || active_page === 'catalog' || active_page === 'thread')) {
|
||||
let renderer = {
|
||||
renderOn: (container) => hcaptcha.render(container, {
|
||||
applyOn: (container, params) => hcaptcha.render(container, {
|
||||
sitekey: "{{ config.hcaptcha_public }}",
|
||||
callback: params['on-success'],
|
||||
}),
|
||||
remove: (widgetId) => { /* Not supported */ },
|
||||
reset: (widgetId) => hcaptcha.reset(widgetId)
|
||||
|
@ -300,10 +301,11 @@ window.onCaptchaLoadTurnstile_post_thread = function() {
|
|||
function onCaptchaLoadTurnstile(action) {
|
||||
if (captcha_renderer === null && (active_page === 'index' || active_page === 'catalog' || active_page === 'thread')) {
|
||||
let renderer = {
|
||||
renderOn: function(container) {
|
||||
applyOn: function(container, params) {
|
||||
let widgetId = turnstile.render('#' + container, {
|
||||
sitekey: "{{ config.turnstile_public }}",
|
||||
action: action,
|
||||
callback: params['on-success'],
|
||||
});
|
||||
if (widgetId === undefined) {
|
||||
return null;
|
||||
|
@ -320,9 +322,16 @@ function onCaptchaLoadTurnstile(action) {
|
|||
{% endif %} // End if turnstile
|
||||
|
||||
function onCaptchaLoad(renderer) {
|
||||
// Initialize the form identifier with a random password.
|
||||
document.getElementById('captcha-form-id').value = generatePassword();
|
||||
|
||||
captcha_renderer = renderer;
|
||||
|
||||
let widgetId = renderer.renderOn('captcha-container');
|
||||
let widgetId = renderer.applyOn('captcha-container', {
|
||||
'on-success': function(token) {
|
||||
document.getElementById('captcha-response').value = token;
|
||||
}
|
||||
});
|
||||
if (widgetId === null) {
|
||||
console.error('Could not render captcha!');
|
||||
}
|
||||
|
|
|
@ -118,6 +118,8 @@
|
|||
</th>
|
||||
<td>
|
||||
<div id="captcha-container"></div>
|
||||
<textarea id="captcha-response" name="captcha-response" style="display:none;"></textarea>
|
||||
<textarea id="captcha-form-id" name="captcha-form-id" style="display:none;"></textarea>
|
||||
{{ antibot.html() }}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue