You are here

function _seckit_origin in Security Kit 7

Same name and namespace in other branches
  1. 6 seckit.module \_seckit_origin()

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 _seckit_origin()
seckit_init in ./seckit.module
Implements hook_init().

File

./seckit.module, line 503
Allows administrators to improve security of the website.

Code

function _seckit_origin() {

  // Allow requests without an 'Origin' header, or with a 'null' origin.
  $origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
  if (!$origin || $origin === 'null') {
    return;
  }

  // Allow GET and HEAD requests.
  $method = $_SERVER['REQUEST_METHOD'];
  if (in_array($method, array(
    'GET',
    'HEAD',
  ), TRUE)) {
    return;
  }

  // Allow requests from localhost.
  if (in_array(ip_address(), array(
    'localhost',
    '127.0.0.1',
    '::1',
  ), TRUE)) {

    // Unless this is a test.
    if (!drupal_valid_test_ua()) {
      return;
    }
  }

  // Allow requests from whitelisted Origins.
  global $base_root;
  $options = _seckit_get_options();
  $whitelist = $options['seckit_csrf']['origin_whitelist'];
  $whitelist[] = $base_root;

  // default origin is always allowed
  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.
  $_POST = array();

  // Log the blocked attack.
  $args = array(
    '@ip' => ip_address(),
    '@origin' => $origin,
  );
  watchdog('seckit', 'Possible CSRF attack was blocked. IP address: @ip, Origin: @origin.', $args, WATCHDOG_WARNING);

  // Deliver the 403 (access denied) error page to the user.
  drupal_access_denied();

  // Abort this request.
  drupal_exit();
}