You are here

public function SecKitEventSubscriber::seckitOrigin in Security Kit 8

Same name and namespace in other branches
  1. 2.x src/EventSubscriber/SecKitEventSubscriber.php \Drupal\seckit\EventSubscriber\SecKitEventSubscriber::seckitOrigin()

Aborts HTTP request upon invalid 'Origin' HTTP request header.

When included in an HTTP request, the Origin header indicates the origin(s) that caused the user agent to issue the request. This helps to protect against CSRF attacks, as we can abort requests with an unapproved origin.

Applies to all HTTP request methods except GET and HEAD.

Requests which do not include an 'Origin' header must always be allowed, as (a) not all user-agents support the header, and (b) those that do may include it or omit it at their discretion.

Note that (a) will become progressively less of a factor over time -- CSRF attacks depend upon convincing a user agent to send a request, and there is no particular motivation for users to prevent their web browsers from sending this header; so as people upgrade to browsers which support 'Origin', its effectiveness increases.

Implementation of Origin is based on specification draft available at http://tools.ietf.org/html/draft-abarth-origin-09

1 call to SecKitEventSubscriber::seckitOrigin()
SecKitEventSubscriber::onKernelRequest in src/EventSubscriber/SecKitEventSubscriber.php
Executes actions on the request event.

File

src/EventSubscriber/SecKitEventSubscriber.php, line 142

Class

SecKitEventSubscriber
Subscribing an event.

Namespace

Drupal\seckit\EventSubscriber

Code

public function seckitOrigin($event) {

  // Allow requests without an 'Origin' header, or with a 'null' origin.
  $origin = $this->request->headers
    ->get('Origin');
  if (!$origin || $origin === 'null') {
    return;
  }

  // Allow command-line requests.
  if (PHP_SAPI === 'cli') {
    return;
  }

  // Allow GET and HEAD requests.
  $method = $this->request
    ->getMethod();
  if (in_array($method, [
    'GET',
    'HEAD',
  ], TRUE)) {
    return;
  }

  // Allow requests from whitelisted Origins.
  global $base_root;
  $whitelist = explode(',', $this->config
    ->get('seckit_csrf.origin_whitelist'));

  // Default origin is always allowed.
  $whitelist[] = $base_root;
  $whitelist = array_values(array_filter(array_map('trim', $whitelist)));
  if (in_array($origin, $whitelist, TRUE)) {
    return;

    // n.b. RFC 6454 allows Origins to have more than one value (each
    // separated by a single space).  All values must be on the whitelist
    // (order is not important).  We intentionally do not handle this
    // because the feature has been confirmed as a design mistake which
    // user agents do not utilise in practice.  For details, see
    // http://lists.w3.org/Archives/Public/www-archive/2012Jun/0001.html
    // and https://www.drupal.org/node/2406075
  }

  // The Origin is invalid, so we deny the request.
  // Clean the POST data first, as drupal_access_denied() may render a page
  // with forms which check for their submissions.
  $args = [
    '@ip' => $this->request
      ->getClientIp(),
    '@origin' => $origin,
  ];
  $message = 'Possible CSRF attack was blocked. IP address: @ip, Origin: @origin.';
  $this->logger
    ->warning($message, $args);
  $event
    ->setResponse(new Response($this
    ->t('Access denied'), Response::HTTP_FORBIDDEN));
}