From patchwork Sat Oct 5 17:47:36 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukas Fleischer X-Patchwork-Id: 1248 Return-Path: Delivered-To: patchwork@archlinux.org Received: from apollo.archlinux.org (localhost [127.0.0.1]) by apollo.archlinux.org (Postfix) with ESMTP id 4E387143CEC30 for ; Sat, 5 Oct 2019 17:47:38 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on apollo X-Spam-Level: X-Spam-Status: No, score=-2.9 required=5.0 tests=DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1,DKIM_VALID=-0.1,DKIM_VALID_AU=-0.1, LOCAL_FAKEBUSINESS=0.5,MAILING_LIST_MULTI=-1,RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001,T_DMARC_POLICY_NONE=0.01,WEIRD_QUOTING=0.001 autolearn=unavailable autolearn_force=no version=3.4.2 X-Spam-BL-Results: [127.0.9.2] Received: from orion.archlinux.org (orion.archlinux.org [IPv6:2a01:4f8:160:6087::1]) by apollo.archlinux.org (Postfix) with ESMTPS for ; Sat, 5 Oct 2019 17:47:38 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id C39A715A40DF7B; Sat, 5 Oct 2019 17:47:29 +0000 (UTC) Received: from luna.archlinux.org (luna.archlinux.org [IPv6:2a01:4f8:160:3033::2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits)) (No client certificate requested) by orion.archlinux.org (Postfix) with ESMTPS; Sat, 5 Oct 2019 17:47:29 +0000 (UTC) Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id A1E5D2BD4B; Sat, 5 Oct 2019 17:47:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=archlinux.org; s=luna2; t=1570297649; bh=rwQEKQ+HWmybg6kHVAh/dqpyFUVAbOT75SGHz3ixEro=; h=From:To:Subject:Date:In-Reply-To:References:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe; b=sG+x85pYKsZwCVvBnzWl3d4ytavoiu7UO1vuD3unY+rMxwW4JIQdi/tAL66x3I8CC yhJ3GrgMpWlZuvR0+nmK3CFErCMrSzNanFTIYRpQhUBOldlOVq4da+yYD89aTiwpaV jcqjZJCMT3fNmZNfvNLtHeN3JY/UQz00gpdYwKBnMJTwkDtCWzDpu+7wmqIKGOHXHb Yp2Ke2c9Wtr8JISLC2fJQCjNWV/cDGZrtXnQo/sehRTF8xwnIy9GZzut/46xPcdBK+ A6xLhD3gjBECUVWEGQIDEjxQ5L7ifhljhUV0NPykJEPfOvqWNqflPArV/SDMhjbQu8 8VCxEc9cAU+2YATOSipVI4Lbg8/CaeElyV2GxzWUOtsuUuZS2EcEbcFwrbDSVSnuDb Ve9dcKBKlvXcxCRTbaYSkWtwFxgds6bQNqDzVwoA08H20DVNoFXSr2Noh74DYQ0KDw dXjGaPh4qdWqTyxDMT2nV4fiB3+FjTd0Hy5pOH+XPsx4zIWuiCS2h/dEwFCtkNJoGt w4IdqWFEWc4OtLc0JihuPi0yI1YgO33KGQb1nyjGVgbSxilmSQ0qrdjVuW8JcsdV59 RALI6hLhFl/8f2Kt0kWEMAh3A/EOJPh+8UKjwLR/7nr28TuWKW7agLDq+eVdQ2Im9m ql8P1NID5GdubfeVrx+Km6pM= Received: from luna.archlinux.org (luna.archlinux.org [127.0.0.1]) by luna.archlinux.org (Postfix) with ESMTP id D75C22BD49 for ; Sat, 5 Oct 2019 17:47:26 +0000 (UTC) Received: from orion.archlinux.org (orion.archlinux.org [IPv6:2a01:4f8:160:6087::1]) by luna.archlinux.org (Postfix) with ESMTPS for ; Sat, 5 Oct 2019 17:47:26 +0000 (UTC) Received: from orion.archlinux.org (localhost [127.0.0.1]) by orion.archlinux.org (Postfix) with ESMTP id 465C415A40DF76 for ; Sat, 5 Oct 2019 17:47:17 +0000 (UTC) Received: from localhost (unknown [72.138.14.22]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) (Authenticated sender: lfleischer) by orion.archlinux.org (Postfix) with ESMTPSA id C349215A40DF75 for ; Sat, 5 Oct 2019 17:47:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=archlinux.org; s=orion; t=1570297637; bh=rwQEKQ+HWmybg6kHVAh/dqpyFUVAbOT75SGHz3ixEro=; h=From:To:Subject:Date:In-Reply-To:References; b=P1NDYFRDP2yzMWt/cFs6ZzHfM3zKo4ziy5I0NMiXlPNtG5dwCNkMHDHdFMAonPgtB EmzUGBVc4WSLVMI97cYKlWMJOwM/kRdP1dJy5oXXkbV7AKF7i7xrtLoRgu3lyOXvxt tB9sawWsoB9hMbpAnvc2VsAPyq8mXpgOcmjD6Nv3mZ+MOB/A17Ab6yTXwo3a1HYP85 uywD+YMLMKxdJvgGUk9VsL+sYmlEUjEpzhwS4pE3AvE9a0Pw1jUkarSzERju2ntEtN OniEa0Hdc+Dn0RwiNNJuQQBOPj8TSBJoCKfQ9P20dOczISeSbh4f0oKX0Hj80MUBKd v1Ha8iVfoW4Wsa8bTrRRxHM+8eZ36VgRYlDee2mmEMANzc6DGwMOD5UXEch6tEGOLr FkRgZ3QURs+3IaAY8yEaw0ZG209nfsoF9+KwTmFL2y39JUJ3191wBkKlMQqcEEE6RO RdYkmbiWUd85unXeZJT5qIQvV9Lziedyj610l6alOFDVpwnmVL84b0JLWbGvdWNi18 p5bfPIjOoqZ48jq5Sa6oPBk9oxvHjS5ui/y9P7HFSdVsVFwxqihm+oAigNwmvBeSfm Xmt/WlygvjfQfJlEzVufN+S7qOoCYDDVYZ6QSoOtHF1/q6e3LKB+ifBpi/K37Jec8J LCXEhUkw7NNjovs2X2iSfYFs= From: Lukas Fleischer To: aur-dev@archlinux.org Subject: [PATCH v2] Add a simple CAPTCHA to the sign up form Date: Sat, 5 Oct 2019 13:47:36 -0400 Message-Id: <20191005174736.56401-1-lfleischer@archlinux.org> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20190906192804.64231-1-lfleischer@archlinux.org> References: <20190906192804.64231-1-lfleischer@archlinux.org> MIME-Version: 1.0 X-BeenThere: aur-dev@archlinux.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Arch User Repository \(AUR\) Development" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: aur-dev-bounces@archlinux.org Sender: "aur-dev" Add a CAPTCHA to protect against automated account creation. The CAPTCHA changes whenever three new accounts are registered. Signed-off-by: Lukas Fleischer --- 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 '

' . __("Use this form to create an account.") . '

'; 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 = "
  • ".$error."
\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 = << + +
+ +

+ + () + +

+
+ +