From ec9e64f0e36cf91a9ff516cd36323c626f5cd81e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Tim=20D=C3=BCsterhus?= Date: Thu, 4 Dec 2014 21:42:16 +0100 Subject: [PATCH] Implement Recaptcha V2 --- com.woltlab.wcf/templates/recaptcha.tpl | 69 +++++++++++++++++++ .../system/captcha/RecaptchaHandler.class.php | 37 ++++++++-- .../recaptcha/RecaptchaHandler.class.php | 32 +-------- .../recaptcha/RecaptchaHandlerV2.class.php | 54 +++++++++++++++ wcfsetup/install/lang/de.xml | 4 ++ wcfsetup/install/lang/en.xml | 4 ++ 6 files changed, 166 insertions(+), 34 deletions(-) create mode 100644 wcfsetup/install/files/lib/system/recaptcha/RecaptchaHandlerV2.class.php diff --git a/com.woltlab.wcf/templates/recaptcha.tpl b/com.woltlab.wcf/templates/recaptcha.tpl index e215b27fe1..2fb196a3e0 100644 --- a/com.woltlab.wcf/templates/recaptcha.tpl +++ b/com.woltlab.wcf/templates/recaptcha.tpl @@ -1,6 +1,8 @@ {if $recaptchaLegacyMode|empty} {include file='captcha'} {else} + {* No explicit keys were set, use legacy V1 API and WoltLab's OEM keys *} + {if RECAPTCHA_PUBLICKEY === '' || RECAPTCHA_PRIVATEKEY === ''}
{lang}wcf.recaptcha.description{/lang} @@ -94,4 +96,71 @@ {/if}
+ {else} +
+ {lang}wcf.recaptcha.title{/lang} + {assign var="recaptchaBucketID" value=true|microtime|sha1} +
+
+
+
+ {if (($errorType|isset && $errorType|is_array && $errorType[recaptchaString]|isset) || ($errorField|isset && $errorField == 'recaptchaString'))} + {if $errorType|is_array && $errorType[recaptchaString]|isset} + {assign var='__errorType' value=$errorType[recaptchaString]} + {else} + {assign var='__errorType' value=$errorType} + {/if} + + {if $__errorType == 'empty'} + {lang}wcf.global.form.error.empty{/lang} + {else} + {lang}wcf.captcha.recaptchaV2.error.recaptchaString.{$__errorType}{/lang} + {/if} + + {/if} +
+
+ +
+ {/if} {/if} diff --git a/wcfsetup/install/files/lib/system/captcha/RecaptchaHandler.class.php b/wcfsetup/install/files/lib/system/captcha/RecaptchaHandler.class.php index 4ac88a0810..847861e542 100644 --- a/wcfsetup/install/files/lib/system/captcha/RecaptchaHandler.class.php +++ b/wcfsetup/install/files/lib/system/captcha/RecaptchaHandler.class.php @@ -30,7 +30,18 @@ class RecaptchaHandler implements ICaptchaHandler { * @see \wcf\system\captcha\ICaptchaHandler::getFormElement() */ public function getFormElement() { - \wcf\system\recaptcha\RecaptchaHandler::getInstance()->assignVariables(); + if (WCF::getSession()->getVar('recaptchaDone')) return ''; + + if (!RECAPTCHA_PUBLICKEY || !RECAPTCHA_PRIVATEKEY) { + // V1 + \wcf\system\recaptcha\RecaptchaHandler::getInstance()->assignVariables(); + } + else { + // V2 + WCF::getTPL()->assign(array( + 'recaptchaLegacyMode' => true + )); + } return WCF::getTPL()->fetch('recaptcha'); } @@ -39,15 +50,22 @@ class RecaptchaHandler implements ICaptchaHandler { * @see \wcf\system\captcha\ICaptchaHandler::isAvailable() */ public function isAvailable() { - return RECAPTCHA_PUBLICKEY && RECAPTCHA_PRIVATEKEY; + return true; } /** * @see \wcf\system\captcha\ICaptchaHandler::readFormParameters() */ public function readFormParameters() { - if (isset($_POST['recaptcha_challenge_field'])) $this->challenge = StringUtil::trim($_POST['recaptcha_challenge_field']); - if (isset($_POST['recaptcha_response_field'])) $this->response = StringUtil::trim($_POST['recaptcha_response_field']); + if (!RECAPTCHA_PUBLICKEY || !RECAPTCHA_PRIVATEKEY) { + // V1 + if (isset($_POST['recaptcha_challenge_field'])) $this->challenge = StringUtil::trim($_POST['recaptcha_challenge_field']); + if (isset($_POST['recaptcha_response_field'])) $this->response = StringUtil::trim($_POST['recaptcha_response_field']); + } + else { + // V2 + if (isset($_POST['g-recaptcha-response'])) $this->response = $_POST['g-recaptcha-response']; + } } /** @@ -61,6 +79,15 @@ class RecaptchaHandler implements ICaptchaHandler { * @see \wcf\system\captcha\ICaptchaHandler::validate() */ public function validate() { - \wcf\system\recaptcha\RecaptchaHandler::getInstance()->validate($this->challenge, $this->response); + if (WCF::getSession()->getVar('recaptchaDone')) return; + + if (!RECAPTCHA_PUBLICKEY || !RECAPTCHA_PRIVATEKEY) { + // V1 + \wcf\system\recaptcha\RecaptchaHandler::getInstance()->validate($this->challenge, $this->response); + } + else { + // V2 + \wcf\system\recaptcha\RecaptchaHandlerV2::getInstance()->validate($this->response); + } } } diff --git a/wcfsetup/install/files/lib/system/recaptcha/RecaptchaHandler.class.php b/wcfsetup/install/files/lib/system/recaptcha/RecaptchaHandler.class.php index 33f5ed4e50..20c0a9b160 100644 --- a/wcfsetup/install/files/lib/system/recaptcha/RecaptchaHandler.class.php +++ b/wcfsetup/install/files/lib/system/recaptcha/RecaptchaHandler.class.php @@ -71,35 +71,9 @@ class RecaptchaHandler extends SingletonFactory { $this->languageCode = 'en'; } - // fetch appropriate keys - $this->publicKey = $this->getKey(RECAPTCHA_PUBLICKEY, 'public'); - $this->privateKey = $this->getKey(RECAPTCHA_PRIVATEKEY, 'private'); - } - - /** - * Returns appropriate public or private key, supports multiple hosts. - * - * @param string $pubKey - * @param string $type - * @return string - */ - protected function getKey($pubKey, $type) { - // check if multiple keys are given - $keys = explode("\n", $pubKey); - if (count($keys) > 1) { - foreach ($keys as $key) { - $keyParts = explode(':', $key); - - if (StringUtil::trim($keyParts[0]) == $_SERVER['HTTP_HOST']) { - return StringUtil::trim($keyParts[1]); - } - } - } - else { - return $pubKey; - } - - throw new SystemException('No valid '.$type.' key for reCAPTCHA found.'); + // WoltLab's V1 OEM keys + $this->publicKey = '6LfOlMYSAAAAADvo3s4puBAYDqI-6YK2ybe7BJE5'; + $this->privateKey = '6LfOlMYSAAAAAKR3m_EFxmDv1xS8PCfeaSZ2LdG9'; } /** diff --git a/wcfsetup/install/files/lib/system/recaptcha/RecaptchaHandlerV2.class.php b/wcfsetup/install/files/lib/system/recaptcha/RecaptchaHandlerV2.class.php new file mode 100644 index 0000000000..8dfc75f478 --- /dev/null +++ b/wcfsetup/install/files/lib/system/recaptcha/RecaptchaHandlerV2.class.php @@ -0,0 +1,54 @@ + + * @package com.woltlab.wcf + * @subpackage system.recaptcha + * @category Community Framework + */ +class RecaptchaHandlerV2 extends SingletonFactory { + /** + * Validates response. + * + * @param string $response + */ + public function validate($response) { + // fail if response is empty to avoid sending api requests + if (empty($response)) { + throw new UserInputException('recaptchaString', 'false'); + } + + $request = new HTTPRequest('https://www.google.com/recaptcha/api/siteverify?secret='.rawurlencode(RECAPTCHA_PRIVATEKEY).'&response='.rawurlencode($response).'&remoteip='.rawurlencode(UserUtil::getIpAddress()), array('timeout' => 10)); + + try { + $request->execute(); + $reply = $request->getReply(); + $data = JSON::decode($reply['body']); + + if ($data['success']) { + // yeah + } + else { + throw new UserInputException('recaptchaString', 'false'); + } + } + catch (SystemException $e) { + // log error, but accept captcha + $e->getExceptionID(); + } + + WCF::getSession()->register('recaptchaDone', true); + } +} diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index d74e6ba29e..7af9e9de9b 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -1801,6 +1801,10 @@ Erlaubte Dateiendungen: {', '|implode:$attachmentHandler->getFormattedAllowedExt + + + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index a256c79421..aaaa34b558 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -1800,6 +1800,10 @@ Allowed extensions: {', '|implode:$attachmentHandler->getFormattedAllowedExtensi + + + + -- 2.20.1