[PATCH] 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 is a first attempt to stop the recent wave of spammers. Other counter-measures will be implemented if it is not effective. web/html/register.php | 14 +++++- web/lib/acctfuncs.inc.php | 74 +++++++++++++++++++++++++++++- web/template/account_edit_form.php | 11 +++++ 3 files changed, 95 insertions(+), 4 deletions(-) diff --git a/web/html/register.php b/web/html/register.php index 368999a..a426482 100644 --- a/web/html/register.php +++ b/web/html/register.php @@ -36,7 +36,12 @@ if (in_request("Action") == "NewAccount") { 0, in_request("CN"), in_request("UN"), - in_request("ON")); + in_request("ON"), + 0, + "", + in_request("captcha_salt"), + in_request("captcha"), + ); print $message; @@ -59,7 +64,12 @@ if (in_request("Action") == "NewAccount") { 0, in_request("CN"), in_request("UN"), - in_request("ON")); + in_request("ON"), + 0, + "", + in_request("captcha_salt"), + in_request("captcha") + ); } } else { print '<p>' . __("Use this form to create an account.") . '</p>'; diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php index dc44484..b0513c6 100644 --- a/web/lib/acctfuncs.inc.php +++ b/web/lib/acctfuncs.inc.php @@ -62,17 +62,25 @@ function html_format_pgp_fingerprint($fingerprint) { * @param string $ON Whether to notify of ownership changes * @param string $UID The user ID of the displayed user * @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 */ 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; if ($TZ == "") { $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"); 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 $UID The user ID of the modified account * @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 */ 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; $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) { $message = "<ul class='errorlist'><li>".$error."</li></ul>\n"; return array(false, $message); @@ -1445,3 +1467,51 @@ function account_comments_count($uid) { $result = $dbh->query($q); 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 "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); +} diff --git a/web/template/account_edit_form.php b/web/template/account_edit_form.php index 38d5274..5e84aa7 100644 --- a/web/template/account_edit_form.php +++ b/web/template/account_edit_form.php @@ -174,6 +174,17 @@ </p> </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> <p> <label></label> -- 2.23.0
On 9/6/19 3:28 PM, Lukas Fleischer wrote:
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 is a first attempt to stop the recent wave of spammers. Other counter-measures will be implemented if it is not effective.
So far seems like it may be helping.
web/html/register.php | 14 +++++- web/lib/acctfuncs.inc.php | 74 +++++++++++++++++++++++++++++- web/template/account_edit_form.php | 11 +++++ 3 files changed, 95 insertions(+), 4 deletions(-)
+ * 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 "pacman -V|sed -r 's#[0-9]+#" . $token . "#g'|md5sum|cut -c1-6"; +}
But I think we need to mention LC_ALL=C here. See e.g. https://bugs.archlinux.org/task/63808 -- Eli Schwartz Bug Wrangler and Trusted User
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> --- We have been using this in production for a while and the amount of spam we got seemed to decline significantly since then. This second version includes "LC_ALL=C" in the pacman command to deal with localized setups. It'll be merged into master soon. web/html/register.php | 14 +++++- web/lib/acctfuncs.inc.php | 74 +++++++++++++++++++++++++++++- web/template/account_edit_form.php | 11 +++++ 3 files changed, 95 insertions(+), 4 deletions(-) diff --git a/web/html/register.php b/web/html/register.php index 368999a..a426482 100644 --- a/web/html/register.php +++ b/web/html/register.php @@ -36,7 +36,12 @@ if (in_request("Action") == "NewAccount") { 0, in_request("CN"), in_request("UN"), - in_request("ON")); + in_request("ON"), + 0, + "", + in_request("captcha_salt"), + in_request("captcha"), + ); print $message; @@ -59,7 +64,12 @@ if (in_request("Action") == "NewAccount") { 0, in_request("CN"), in_request("UN"), - in_request("ON")); + in_request("ON"), + 0, + "", + in_request("captcha_salt"), + in_request("captcha") + ); } } else { print '<p>' . __("Use this form to create an account.") . '</p>'; diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php index dc44484..f9378fe 100644 --- a/web/lib/acctfuncs.inc.php +++ b/web/lib/acctfuncs.inc.php @@ -62,17 +62,25 @@ function html_format_pgp_fingerprint($fingerprint) { * @param string $ON Whether to notify of ownership changes * @param string $UID The user ID of the displayed user * @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 */ 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; if ($TZ == "") { $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"); 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 $UID The user ID of the modified account * @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 */ 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; $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) { $message = "<ul class='errorlist'><li>".$error."</li></ul>\n"; return array(false, $message); @@ -1445,3 +1467,51 @@ function account_comments_count($uid) { $result = $dbh->query($q); 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); +} diff --git a/web/template/account_edit_form.php b/web/template/account_edit_form.php index 38d5274..5e84aa7 100644 --- a/web/template/account_edit_form.php +++ b/web/template/account_edit_form.php @@ -174,6 +174,17 @@ </p> </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> <p> <label></label> -- 2.23.0
participants (2)
-
Eli Schwartz
-
Lukas Fleischer