In some cases we get a "CAPTCHA session reuse attack detected." in our logs. I suspect that this could i some cases be due to the fact that the user have spent a long time on the site before submitting the form. Eg. reading a lot of text first.

The standard setting for PHP session.gc_maxlifetime is 1440 seconds. Also the table holding the sessions "captcha_sessions" is purged via cron, where records older than session.gc_maxlifetime are deleted.

So if a user loads a page with captcha (we are using the Math captcha) and spends a lot of time on the page. Then decides to submit something it is possible that cron has already run, and deleted the users corresponding record with token information related to the captcha. The captcha module then responds with a "CAPTCHA session reuse attack detected." message.

The user has to spend more than 1440 secods on the page, but then this could happen.

Issue fork captcha-3488136

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

candalt created an issue. See original summary.

rene bakx’s picture

I have the same issue at a website with a rather big multistep form and did noticed a older closed issue (https://www.drupal.org/project/captcha/issues/3423309) that most likely is related to this issue as well.

The default services.yml set's that value to gc_maxlifetime: 200000 so that timeout should not occur at all, but that is not always the case!

If you run your cronjob's via drush, that services.yml is not loaded and the default value is session.gc_maxlifetime => 1440 => 1440

Running the cron every 5 minutes, gives a rather big change of triggering deleting a session that is still in use, as this module uses the value from PHP. This can be easily checked by adding single line to the captcha_cron() method

function captcha_cron() {
  \Drupal::logger('Cron')->info('Captcha cron max life time '.ini_get('session.gc_maxlifetime'));
  // Rest of the code.
}

Drush cron logs : Captcha cron max life time 1440
Browser/curl/wget cron logs: Captcha cron max life time 200000

Multiple options to fix this issue.

Personally I would opt-in for making the delete time configurable in the Captcha module itself with a default value being the value from ini_get('session.gc_maxlifetime'). Doing this would not be a breaking change, and it makes sure the value is consistent with the core session garbage collection as this is triggered by PHP’s session system during normal web requests,

The other option would be to change your PHP settings globally to the same value as set in services.yml, but that is not possible on all hosting providers.

And lastly you could opt-in to run the cron using the wget/curl approach, but that has it downsides when running in a walled or airgap environment.

So no hard solution, but an open discussion about the approach for this issue.

vmarchuk’s picture

Version: 2.0.6 » 2.x-dev
vmarchuk’s picture

Thank you for reporting the issue! I think we have the same annoying problem, but it's hard to say yet.
We'll try using the code from MR that I opened and see if that helps. This shouldn't break anything or have any side effects.
I will report the results after using the patch for some time.

anybody’s picture

Status: Active » Needs work

Tests are failing?

japerry made their first commit to this issue’s fork.

japerry’s picture

Status: Needs work » Fixed

The diagnosis in #2 is right and MR !136 reads the container parameter instead, which is the right fix. I rebased it onto current 2.x and added a commit to clean it up a little:

  • If session.storage.options does not contain gc_maxlifetime (a site's services.yml replaces the whole parameter, so this can happen), cron falls back to ini_get() instead of hitting an undefined array key.
  • CaptchaCronTest was the CI failure: it still set the cutoff with ini_set(), which cron no longer reads. It now sets PHP's 1440-second default, the value Drush sees, and checks that a challenge older than that but within Drupal's lifetime survives cron.
  • New kernel test, CaptchaCronSessionLifetimeTest, covers both a configured gc_maxlifetime and the fallback. It fails against current 2.x.

Fixed!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.