Skip to content

ReCAPTCHA on User Lost Password Page #11214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: stable-3_3_0
Choose a base branch
from

Conversation

Godoy0722
Copy link
Contributor

Description: Add ReCAPTCHA on the userLostPassword.tpl page. Solves the issue #6984.

$this->addJavaScript(
'recaptcha',
'https://www.recaptcha.net/recaptcha/api.js?hl=' . substr(AppLocale::getLocale(),0,2),
[
'contexts' => ['frontend-user-register', 'frontend-user-registerUser'],
'contexts' => ['frontend-user-register', 'frontend-user-registerUser', 'frontend-login-lostPassword'],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. I'll send a new commit with these updates. Thanks @jonasraoni !

$captchaEnabled = Config::getVar('captcha', 'captcha_on_password_reset') && Config::getVar('captcha', 'recaptcha');
if ($captchaEnabled) {
import('lib.pkp.classes.form.Form');
$form = new Form('');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$form = new Form('');
$form = new Form();

$email = $request->getUserVar('email');
$userDao = DAORegistry::getDAO('UserDAO'); /** @var UserDAO $userDao */
$user = $userDao->getUserByEmail($email);

$rateLimitingEnabled = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be added to a configuration, and perhaps disabled by default to avoid breaking changes.
Then a user can opt-in for both the recaptcha and the rate limiter.

$session = $sessionManager->getUserSession();

// Store reset requests with timestamps in the session
$resetRequests = $session->getSessionVar('passwordResetRequests') ?: [];
Copy link
Contributor

@jonasraoni jonasraoni Apr 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, if we store the retries on the session, then I think it's not very useful, as clearing the session cookie will be enough to skip the check.

I think this piece of code (rate limiter) should be removed or moved to another issue, there's a relevant discussion here: #11084

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. I'll remove this piece of code for now.

Comment on lines 43 to 49
{if $captchaEnabled}
<div class="captcha">
<div class="pkp_form_locale_field">
{$reCaptchaHtml}
</div>
</div>
{/if}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to follow the pattern of other places:

Suggested change
{if $captchaEnabled}
<div class="captcha">
<div class="pkp_form_locale_field">
{$reCaptchaHtml}
</div>
</div>
{/if}
{if $reCaptchaHtml}
<div class="captcha">
<div class="pkp_form_locale_field">
{$reCaptchaHtml}
</div>
</div>
{/if}


if (!$captchaValidator->isValid()) {
$templateMgr->assign([
'captchaEnabled' => true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'captchaEnabled' => true,

}

// Check if this email has a recent request (within the last 10 minutes)
if (isset($resetRequests[$email]) && ($currentTime - $resetRequests[$email] < 600)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user was blocked, I think it's good to inform that a new request can be made in "X minutes"


// Remove entries older than 1 hour (3600 seconds)
foreach ($resetRequests as $resetEmail => $timestamp) {
if ($currentTime - $timestamp > 3600) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the user is allowed to try again after 10 minutes, then I think we can clear the entries also after 10 minutes.

Copy link
Contributor

@jonasraoni jonasraoni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@asmecher I'm not sure if the rate limiter should be added as part of this issue, but if we continue, perhaps it should be behind a configuration (could be even enabled by default, given the attacks which are happening out there), and we can add more settings (e.g. freezing time).

We should also include the reCaptcha on the login form (a feature that was added just to 3.4: #6539).

@Godoy0722
Copy link
Contributor Author

Godoy0722 commented May 6, 2025

Hello @jonasraoni, just a heads-up that I implemented your change requests, as far as I also removed the rate limiter as you previously mentioned. Please let me know if I have something else to do in this issue and I'll update it. 😄

P.S.: I also implemented the Google ReCAPTCHA on the login page, as you mentioned in your comment above for the issue #6539.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants