In a module I am writing, I need to add a hidden field in the registration form, similarly to what the Honeypot module does.

$form['honeypot_time'] = [
  '#type' => 'hidden',
  '#title' => t('Timestamp'),
  '#default_value' => mymodule_get_signed_timestamp(time()),
  '#element_validate' => ['mymodule_signed_timestamp_validate'],
  '#cache' => [
    'max-age' => 0,

mymodule_get_signed_timestamp() concatenates the timestamp with a value used to avoid the timestamp is arbitrary changed. It could be computed in one of two ways:

  1. Using Crypt::hmacBase64()
  2. Using the csrf_token service, which by default provides CsrfTokenGenerator::get() and CsrfTokenGenerator::validate()

The choose is then between one of the following snippets.

use \Drupal\Component\Utility;

$token = Crypt::hmacBase64($value, \Drupal::service('private_key')->get());
use \Drupal\Core\Access;

$token = \Drupal::csrfToken()->get($value);

The difference is apparently just that the csrf_token service's methods use, as part of the key passed to Crypt::hmacBase64(), also the session's data and the hash salt set in the settings.php file.

Considering that, in most of the cases, I would be altering a form submitted from anonymous users who tries to register an account (but I cannot exclude I could be adding that form element to forms submitted from registered users), should I use the first method or the second one?


I made a quick test using the following code.

use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Drupal\Core\Site\Settings;
use Drupal\Core\Access\CsrfToken;
use Drupal\Component\Utility\Crypt;

$autoloader = require_once 'autoload.php';

 * Global flag to identify update.php and authorize.php runs.
 * Identifies update.php and authorize.php runs, avoiding unwanted operations
 * such as css/js preprocessing and translation, and solves some theming issues.
 * The flag is checked in other places in Drupal code (not just authorize.php).
const MAINTENANCE_MODE = 'update';

try {
  $request = Request::createFromGlobals();
  $kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
catch (HttpExceptionInterface $e) {
  $response = new Response('', $e->getStatusCode());

\Drupal::moduleHandler()->addModule('system', 'core/modules/system');
\Drupal::moduleHandler()->addModule('user', 'core/modules/user');


$csrf_token = \Drupal::csrfToken();

$content[] = [
  '#markup' => t('First method: %token.<br />', ['%token' => Crypt::hmacBase64(123456, \Drupal::service('private_key')->get())]),
  '#cache' => ['max-age' => 0],
$content[] = [
  '#markup' => t('Second method: %token.<br />', ['%token' => $csrf_token->get(123456)]),
  '#cache' => ['max-age' => 0],

$renderer = \Drupal::service('bare_html_page_renderer');
$response = $renderer->renderBarePage($content, 'Test results', 'maintenance_page', array(
  '#show_messages' => FALSE,


I then opened three different Firefox windows pointing them at the test script I wrote. This is what I got:

First window

First method: r3xavuZbwgsk1RJTPm-zOs3kUSeSY4-BDnj-UCwgyr0.
Second method: OndzLnnASZLsuzABJltD_HwK8dr48fEW_hdWG9Lu02I.

Second window

First method: r3xavuZbwgsk1RJTPm-zOs3kUSeSY4-BDnj-UCwgyr0.
Second method: J6oHkKdXeL9RUKHX4B1Sn418Yh8XD0tJbRrs459BI7A.

Third window

First method: r3xavuZbwgsk1RJTPm-zOs3kUSeSY4-BDnj-UCwgyr0.
Second method: TYvxZiF7xS6hD2nn8pfHu0GR9OPw0X8xT1HfVDNWkXc.

With the second method, different users are going to get different values, even in the case the input value is the same.
I would say that using the csrf_token service is preferable, in my case.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.