You are here

function _captcha_get_posted_captcha_info in CAPTCHA 8

Same name and namespace in other branches
  1. 6.2 captcha.module \_captcha_get_posted_captcha_info()
  2. 7 captcha.module \_captcha_get_posted_captcha_info()

Helper function for getting the posted CAPTCHA info.

This function hides the form processing mess for several use cases an browser bug workarounds. For example: $element['#post'] can typically be used to get the posted form_id and captcha_sid, but in the case of node preview situations (with correct CAPTCHA response) that does not work and we can get them from $form_state['clicked_button']['#post']. However with Internet Explorer 7, the latter does not work either when submitting the forms (with only one text field) with the enter key (see http://drupal.org/node/534168), in which case we also have to check $form_state['buttons']['button']['0']['#post'].

Parameters

array $element: The CAPTCHA element.

Drupal\Core\Form\FormStateInterface $form_state: The form state structure to extract the info from.

string $this_form_id: The form ID of the form we are currently processing (which is not necessarily the form that was posted).

Return value

array Array with $posted_form_id and $post_captcha_sid (with NULL values if the values could not be found, e.g. for a fresh form).

1 call to _captcha_get_posted_captcha_info()
Captcha::processCaptchaElement in src/Element/Captcha.php
Process callback for CAPTCHA form element.

File

./captcha.module, line 396
This module enables basic CAPTCHA functionality.

Code

function _captcha_get_posted_captcha_info(array $element, FormStateInterface $form_state, $this_form_id) {
  if ($form_state
    ->isSubmitted() && $form_state
    ->has('captcha_info')) {

    // We are handling (or rebuilding) an already submitted form,
    // so we already determined the posted form ID and CAPTCHA session ID
    // for this form (from before submitting). Reuse this info.
    $posted_form_id = $form_state
      ->get('captcha_info')['posted_form_id'];
    $posted_captcha_sid = $form_state
      ->get('captcha_info')['captcha_sid'];
  }
  else {

    // We have to determine the posted form ID and CAPTCHA session ID
    // from the post data.
    // Because we possibly use raw post data here,
    // we should be extra cautious and filter this data.
    $input =& $form_state
      ->getUserInput();
    $posted_form_id = isset($input['form_id']) ? preg_replace("/[^a-z0-9_-]/", "", (string) $input['form_id']) : NULL;
    $posted_captcha_sid = isset($input['captcha_sid']) ? (int) $input['captcha_sid'] : NULL;
    $posted_captcha_token = isset($input['captcha_token']) ? preg_replace("/[^a-zA-Z0-9-_]/", "", (string) $input['captcha_token']) : NULL;
    if ($posted_form_id == $this_form_id) {

      // Check if the posted CAPTCHA token is valid for the posted CAPTCHA
      // session ID. Note that we could just check the validity of the CAPTCHA
      // token and extract the CAPTCHA session ID from that (without looking at
      // the actual posted CAPTCHA session ID). However, here we check both
      // the posted CAPTCHA token and session ID: it is a bit more stringent
      // and the database query should also be more efficient (because there is
      // an index on the CAPTCHA session ID).
      if ($posted_captcha_sid != NULL) {
        $expected_captcha_token = \Drupal::database()
          ->select('captcha_sessions', 'cs')
          ->fields('cs', [
          'token',
        ])
          ->condition('csid', $posted_captcha_sid)
          ->execute()
          ->fetchField();

        // If we do have a CAPTCHA token mismatch then log it.
        try {
          if ($expected_captcha_token !== $posted_captcha_token && empty($input['captcha_cacheable'])) {
            throw new \UnexpectedValueException('CAPTCHA session reuse attack detected.');
          }
        } catch (\Exception $e) {
          \Drupal::logger('captcha')
            ->debug('CAPTCHA session reuse attack detected on @form_id <br/>Posted CAPTCHA token: @posted_captcha_token <br/>Expected captcha token: @expected_captcha_token', [
            '@form_id' => $this_form_id,
            '@expected_captcha_token' => var_export($expected_captcha_token, TRUE),
            '@posted_captcha_token' => var_export($posted_captcha_token, TRUE),
          ]);

          // Invalidate the CAPTCHA session.
          $posted_captcha_sid = NULL;
        }
      }
    }
    else {

      // The CAPTCHA session ID is specific to the posted form.
      // Return NULL, so a new session will be generated for this other form.
      $posted_captcha_sid = NULL;
    }
  }
  return [
    $posted_form_id,
    $posted_captcha_sid,
  ];
}