Add a simple CAPTCHA to the sign up form

Add a CAPTCHA to protect against automated account creation. The CAPTCHA
changes whenever three new accounts are registered.

Signed-off-by: Lukas Fleischer <lfleischer@archlinux.org>
This commit is contained in:
Lukas Fleischer 2019-09-06 14:34:38 -04:00
parent a66c7fa615
commit d6ae970785
3 changed files with 95 additions and 4 deletions

View file

@ -36,7 +36,12 @@ if (in_request("Action") == "NewAccount") {
0, 0,
in_request("CN"), in_request("CN"),
in_request("UN"), in_request("UN"),
in_request("ON")); in_request("ON"),
0,
"",
in_request("captcha_salt"),
in_request("captcha"),
);
print $message; print $message;
@ -59,7 +64,12 @@ if (in_request("Action") == "NewAccount") {
0, 0,
in_request("CN"), in_request("CN"),
in_request("UN"), in_request("UN"),
in_request("ON")); in_request("ON"),
0,
"",
in_request("captcha_salt"),
in_request("captcha")
);
} }
} else { } else {
print '<p>' . __("Use this form to create an account.") . '</p>'; print '<p>' . __("Use this form to create an account.") . '</p>';

View file

@ -62,17 +62,25 @@ function html_format_pgp_fingerprint($fingerprint) {
* @param string $ON Whether to notify of ownership changes * @param string $ON Whether to notify of ownership changes
* @param string $UID The user ID of the displayed user * @param string $UID The user ID of the displayed user
* @param string $N The username as present in the database * @param string $N The username as present in the database
* @param string $captcha_salt The salt used for the CAPTCHA.
* @param string $captcha The CAPTCHA answer.
* *
* @return void * @return void
*/ */
function display_account_form($A,$U="",$T="",$S="",$E="",$H="",$P="",$C="",$R="", function display_account_form($A,$U="",$T="",$S="",$E="",$H="",$P="",$C="",$R="",
$L="",$TZ="",$HP="",$I="",$K="",$PK="",$J="",$CN="",$UN="",$ON="",$UID=0,$N="") { $L="",$TZ="",$HP="",$I="",$K="",$PK="",$J="",$CN="",$UN="",$ON="",$UID=0,$N="",$captcha_salt="",$captcha="") {
global $SUPPORTED_LANGS; global $SUPPORTED_LANGS;
if ($TZ == "") { if ($TZ == "") {
$TZ = config_get("options", "default_timezone"); $TZ = config_get("options", "default_timezone");
} }
if ($captcha_salt != get_captcha_salt()) {
$captcha_salt = get_captcha_salt();
$captcha = "";
}
$captcha_challenge = get_captcha_challenge($captcha_salt);
include("account_edit_form.php"); include("account_edit_form.php");
return; return;
} }
@ -103,11 +111,13 @@ function display_account_form($A,$U="",$T="",$S="",$E="",$H="",$P="",$C="",$R=""
* @param string $ON Whether to notify of ownership changes * @param string $ON Whether to notify of ownership changes
* @param string $UID The user ID of the modified account * @param string $UID The user ID of the modified account
* @param string $N The username as present in the database * @param string $N The username as present in the database
* @param string $captcha_salt The salt used for the CAPTCHA.
* @param string $captcha The CAPTCHA answer.
* *
* @return array Boolean indicating success and message to be printed * @return array Boolean indicating success and message to be printed
*/ */
function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="", function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C="",
$R="",$L="",$TZ="",$HP="",$I="",$K="",$PK="",$J="",$CN="",$UN="",$ON="",$UID=0,$N="") { $R="",$L="",$TZ="",$HP="",$I="",$K="",$PK="",$J="",$CN="",$UN="",$ON="",$UID=0,$N="",$captcha_salt="",$captcha="") {
global $SUPPORTED_LANGS; global $SUPPORTED_LANGS;
$error = ''; $error = '';
@ -269,6 +279,18 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$H="",$P="",$C=""
} }
} }
if (!$error && $TYPE == "new" && empty($captcha)) {
$error = __("The CAPTCHA is missing.");
}
if (!$error && $TYPE == "new" && $captcha_salt != get_captcha_salt()) {
$error = __("This CAPTCHA has expired. Please try again.");
}
if (!$error && $TYPE == "new" && $captcha != get_captcha_answer($captcha_salt)) {
$error = __("The entered CAPTCHA answer is invalid.");
}
if ($error) { if ($error) {
$message = "<ul class='errorlist'><li>".$error."</li></ul>\n"; $message = "<ul class='errorlist'><li>".$error."</li></ul>\n";
return array(false, $message); return array(false, $message);
@ -1445,3 +1467,51 @@ function account_comments_count($uid) {
$result = $dbh->query($q); $result = $dbh->query($q);
return $result->fetchColumn(); return $result->fetchColumn();
} }
/*
* Compute the CAPTCHA salt. The salt changes based on the number of registered
* users. This ensures that new users always use a different salt.
*
* @return string The current salt.
*/
function get_captcha_salt() {
$dbh = DB::connect();
$q = "SELECT count(*) FROM Users";
$result = $dbh->query($q);
$user_count = $result->fetchColumn();
return 'aurweb-' . floor($user_count / 3);
}
/*
* Return the CAPTCHA challenge for a given salt.
*
* @param string $salt The salt to be used for the CAPTCHA computation.
*
* @return string The challenge as a string.
*/
function get_captcha_challenge($salt) {
$token = substr(md5($salt), 0, 3);
return "LC_ALL=C pacman -V|sed -r 's#[0-9]+#" . $token . "#g'|md5sum|cut -c1-6";
}
/*
* Compute CAPTCHA answer for a given salt.
*
* @param string $salt The salt to be used for the CAPTCHA computation.
*
* @return string The correct answer as a string.
*/
function get_captcha_answer($salt) {
$token = substr(md5($salt), 0, 3);
$text = <<<EOD
.--. Pacman v$token.$token.$token - libalpm v$token.$token.$token
/ _.-' .-. .-. .-. Copyright (C) $token-$token Pacman Development Team
\ '-. '-' '-' '-' Copyright (C) $token-$token Judd Vinet
'--'
This program may be freely redistributed under
the terms of the GNU General Public License.
EOD;
return substr(md5($text . "\n"), 0, 6);
}

View file

@ -174,6 +174,17 @@
</p> </p>
</fieldset> </fieldset>
<?php if ($A != "UpdateAccount"): ?>
<fieldset>
<legend><?= __("To protect the AUR against automated account creation, we kindly ask you to provide the output of the following command:") ?> <code><?= htmlspecialchars($captcha_challenge) ?></code></legend>
<p>
<label for="id_captcha"><?= __("Answer") ?>:</label>
<input type="text" size="30" maxlength="6" name="captcha" id="id_captcha" value="<?= htmlspecialchars($captcha, ENT_QUOTES) ?>" /> (<?= __("required") ?>)
<input type="hidden" name="captcha_salt" value="<?= htmlspecialchars($captcha_salt) ?>" />
</p>
</fieldset>
<?php endif; ?>
<fieldset> <fieldset>
<p> <p>
<label></label> <label></label>