SessionConfiguration.php 4.76 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
<?php

namespace Drupal\Core\Session;

use Symfony\Component\HttpFoundation\Request;

/**
 * Defines the default session configuration generator.
 */
class SessionConfiguration implements SessionConfigurationInterface {

  /**
   * An associative array of session ini settings.
   */
  protected $options;

  /**
   * Constructs a new session configuration instance.
   *
   * @param array $options
   *   An associative array of session ini settings.
   *
   * @see \Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage::__construct()
   * @see http://php.net/manual/session.configuration.php
   */
  public function __construct($options = []) {
    $this->options = $options;
  }

  /**
   * {@inheritdoc}
   */
  public function hasSession(Request $request) {
    return $request->cookies->has($this->getName($request));
  }

  /**
   * {@inheritdoc}
   */
  public function getOptions(Request $request) {
    $options = $this->options;

    // Generate / validate the cookie domain.
    $options['cookie_domain'] = $this->getCookieDomain($request) ?: '';

    // If the site is accessed via SSL, ensure that the session cookie is
    // issued with the secure flag.
    $options['cookie_secure'] = $request->isSecure();

    // Set the session cookie name.
    $options['name'] = $this->getName($request);

    return $options;
  }

  /**
   * Returns the session cookie name.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request.
   *
   * @return string
   *   The name of the session cookie.
   */
  protected function getName(Request $request) {
    // To prevent session cookies from being hijacked, a user can configure the
    // SSL version of their website to only transfer session cookies via SSL by
    // using PHP's session.cookie_secure setting. The browser will then use two
    // separate session cookies for the HTTPS and HTTP versions of the site. So
    // we must use different session identifiers for HTTPS and HTTP to prevent a
    // cookie collision.
    $prefix = $request->isSecure() ? 'SSESS' : 'SESS';
    return $prefix . $this->getUnprefixedName($request);
  }

  /**
   * Returns the session cookie name without the secure/insecure prefix.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request.
   *
   * @returns string
   *   The session name without the prefix (SESS/SSESS).
   */
  protected function getUnprefixedName(Request $request) {
    if ($test_prefix = $this->drupalValidTestUa()) {
      $session_name = $test_prefix;
    }
    elseif (isset($this->options['cookie_domain'])) {
      // If the user specifies the cookie domain, also use it for session name.
      $session_name = $this->options['cookie_domain'];
    }
    else {
      // Otherwise use $base_url as session name, without the protocol
      // to use the same session identifiers across HTTP and HTTPS.
      $session_name = $request->getHost() . $request->getBasePath();
      // Replace "core" out of session_name so core scripts redirect properly,
      // specifically install.php.
      $session_name = preg_replace('#/core$#', '', $session_name);
    }

    return substr(hash('sha256', $session_name), 0, 32);
  }

  /**
   * Return the session cookie domain.
   *
   * The Set-Cookie response header and its domain attribute are defined in RFC
109
   * 2109, RFC 2965 and RFC 6265 each one superseding the previous version.
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
   *
   * @see http://tools.ietf.org/html/rfc2109
   * @see http://tools.ietf.org/html/rfc2965
   * @see http://tools.ietf.org/html/rfc6265
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request.
   *
   * @returns string
   *   The session cookie domain.
   */
  protected function getCookieDomain(Request $request) {
    if (isset($this->options['cookie_domain'])) {
      $cookie_domain = $this->options['cookie_domain'];
    }
    else {
      $host = $request->getHost();
      // To maximize compatibility and normalize the behavior across user
      // agents, the cookie domain should start with a dot.
      $cookie_domain = '.' . $host;
    }

    // Cookies for domains without an embedded dot will be rejected by user
    // agents in order to defeat malicious websites attempting to set cookies
    // for top-level domains. Also IP addresses may not be used in the domain
    // attribute of a Set-Cookie header.
    if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
      return $cookie_domain;
    }
  }

  /**
   * Wraps drupal_valid_test_ua().
   *
144
   * @return string|false
145 146 147 148 149 150 151 152 153
   *   Either the simpletest prefix (the string "simpletest" followed by any
   *   number of digits) or FALSE if the user agent does not contain a valid
   *   HMAC and timestamp.
   */
  protected function drupalValidTestUa() {
    return drupal_valid_test_ua();
  }

}