CsrfTokenGenerator.php 3.23 KB
Newer Older
1 2 3 4 5 6
<?php

namespace Drupal\Core\Access;

use Drupal\Component\Utility\Crypt;
use Drupal\Core\PrivateKey;
7
use Drupal\Core\Session\MetadataBag;
8
use Drupal\Core\Site\Settings;
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

/**
 * Generates and validates CSRF tokens.
 *
 * @see \Drupal\Tests\Core\Access\CsrfTokenGeneratorTest
 */
class CsrfTokenGenerator {

  /**
   * The private key service.
   *
   * @var \Drupal\Core\PrivateKey
   */
  protected $privateKey;

24 25 26 27 28 29 30
  /**
   * The session metadata bag.
   *
   * @var \Drupal\Core\Session\MetadataBag
   */
  protected $sessionMetadata;

31 32 33 34 35
  /**
   * Constructs the token generator.
   *
   * @param \Drupal\Core\PrivateKey $private_key
   *   The private key service.
36 37
   * @param \Drupal\Core\Session\MetadataBag $session_metadata
   *   The session metadata bag.
38
   */
39
  public function __construct(PrivateKey $private_key, MetadataBag $session_metadata) {
40
    $this->privateKey = $private_key;
41
    $this->sessionMetadata = $session_metadata;
42 43 44 45 46
  }

  /**
   * Generates a token based on $value, the user session, and the private key.
   *
47
   * The generated token is based on the session of the current user. Normally,
48 49 50 51
   * anonymous users do not have a session, so the generated token will be
   * different on every page request. To generate a token for users without a
   * session, manually start a session prior to calling this function.
   *
52 53 54 55
   * @param string $value
   *   (optional) An additional value to base the token on.
   *
   * @return string
56
   *   A 43-character URL-safe token for validation, based on the token seed,
57
   *   the hash salt provided by Settings::getHashSalt(), and the
58 59
   *   'drupal_private_key' configuration variable.
   *
60
   * @see \Drupal\Core\Site\Settings::getHashSalt()
61
   * @see \Symfony\Component\HttpFoundation\Session\SessionInterface::start()
62 63
   */
  public function get($value = '') {
64 65 66 67
    $seed = $this->sessionMetadata->getCsrfTokenSeed();
    if (empty($seed)) {
      $seed = Crypt::randomBytesBase64();
      $this->sessionMetadata->setCsrfTokenSeed($seed);
68 69
    }

70
    return $this->computeToken($seed, $value);
71 72 73 74 75 76 77 78 79 80 81
  }

  /**
   * Validates a token based on $value, the user session, and the private key.
   *
   * @param string $token
   *   The token to be validated.
   * @param string $value
   *   (optional) An additional value to base the token on.
   *
   * @return bool
82
   *   TRUE for a valid token, FALSE for an invalid token.
83
   */
84
  public function validate($token, $value = '') {
85 86
    $seed = $this->sessionMetadata->getCsrfTokenSeed();
    if (empty($seed)) {
87 88 89
      return FALSE;
    }

90
    return Crypt::hashEquals($this->computeToken($seed, $value), $token);
91 92 93 94 95 96 97 98 99 100 101 102
  }

  /**
   * Generates a token based on $value, the token seed, and the private key.
   *
   * @param string $seed
   *   The per-session token seed.
   * @param string $value
   *   (optional) An additional value to base the token on.
   *
   * @return string
   *   A 43-character URL-safe token for validation, based on the token seed,
103
   *   the hash salt provided by Settings::getHashSalt(), and the
104
   *   'drupal_private_key' configuration variable.
105 106
   *
   * @see \Drupal\Core\Site\Settings::getHashSalt()
107 108
   */
  protected function computeToken($seed, $value = '') {
109
    return Crypt::hmacBase64($value, $seed . $this->privateKey->get() . Settings::getHashSalt());
110 111 112
  }

}