diff --git a/core/core.services.yml b/core/core.services.yml
index 6a0669c..23266fe 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -241,7 +241,7 @@ services:
       - [setFinalMatcher, ['@router.matcher.final_matcher']]
   url_generator:
     class: Drupal\Core\Routing\UrlGenerator
-    arguments: ['@router.route_provider', '@path_processor_manager', '@config.factory', '@settings']
+    arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@config.factory', '@settings']
     calls:
       - [setRequest, ['@?request']]
       - [setContext, ['@?router.request_context']]
@@ -418,6 +418,11 @@ services:
     class: Drupal\Core\Theme\ThemeAccessCheck
     tags:
       - { name: access_check }
+  access_check.csrf:
+    class: Drupal\Core\Access\CsrfAccessCheck
+    tags:
+      - { name: access_check }
+    arguments: ['@csrf_token']
   maintenance_mode_subscriber:
     class: Drupal\Core\EventSubscriber\MaintenanceModeSubscriber
     tags:
@@ -480,6 +485,8 @@ services:
     tags:
       - { name: event_subscriber }
     arguments: [['@exception_controller', execute]]
+  route_processor_manager:
+    class: Drupal\Core\RouteProcessor\RouteProcessorManager
   path_processor_manager:
     class: Drupal\Core\PathProcessor\PathProcessorManager
   path_processor_decode:
@@ -498,6 +505,11 @@ services:
       - { name: path_processor_inbound, priority: 100 }
       - { name: path_processor_outbound, priority: 300 }
     arguments: ['@path.alias_manager']
+  route_processer_csrf:
+    class: Drupal\Core\Access\RouteProcessorCsrf
+    tags:
+      - { name: route_processor_outbound, priority: 400 }
+    arguments: ['@csrf_token']
   transliteration:
     class: Drupal\Core\Transliteration\PHPTransliteration
   flood:
diff --git a/core/lib/Drupal/Core/Access/CsrfAccessCheck.php b/core/lib/Drupal/Core/Access/CsrfAccessCheck.php
new file mode 100644
index 0000000..ceeeb6c
--- /dev/null
+++ b/core/lib/Drupal/Core/Access/CsrfAccessCheck.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Access\CsrfAccessCheck.
+ */
+
+namespace Drupal\Core\Access;
+
+use Drupal\Core\Access\CsrfTokenGenerator;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Allows access to routes to be controlled by a '_csrf' parameter.
+ *
+ * To use this check, add a "csrf" GET parameter to URLs of which the value is a
+ * token generated by \Drupal::csrfToken()->get() using the same value as the
+ * "_csrf" parameter in the route.
+ */
+class CsrfAccessCheck implements StaticAccessCheckInterface {
+
+  /**
+   * The CSRF token generator.
+   *
+   * @var \Drupal\Core\Access\CsrfTokenGenerator
+   */
+  protected $csrfToken;
+
+  /**
+   * Constructs a CsrfAccessCheck object.
+   *
+   * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
+   *   The CSRF token generator.
+   */
+  function __construct(CsrfTokenGenerator $csrf_token) {
+    $this->csrfToken = $csrf_token;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function appliesTo() {
+    return array('_csrf');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    // If this is the controller request, check CSRF access as normal.
+    if ($request->attributes->get('_controller_request')) {
+      return $this->csrfToken->validate($request->query->get('csrf'), $route->getRequirement('_csrf')) ? static::ALLOW : static::KILL;
+    }
+
+    // Otherwise, this could be another requested access check that we don't
+    // want to check CSRF tokens on.
+    $conjunction = $route->getOption('_access_mode') ?: 'ANY';
+    // Return ALLOW if all access checks are needed.
+    if ($conjunction == 'ALL') {
+      return static::ALLOW;
+    }
+    // Return DENY otherwise, as another access checker should grant access
+    // for the route.
+    else {
+      return static::DENY;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php b/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php
new file mode 100644
index 0000000..49cef65
--- /dev/null
+++ b/core/lib/Drupal/Core/Access/RouteProcessorCsrf.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Access\RouteProcessorCsrf.
+ */
+
+namespace Drupal\Core\Access;
+
+use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
+use Drupal\Core\Access\CsrfTokenGenerator;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Processes the inbound path by resolving it to the front page if empty.
+ */
+class RouteProcessorCsrf implements OutboundRouteProcessorInterface {
+
+  /**
+   * The CSRF token generator.
+   *
+   * @var \Drupal\Core\Access\CsrfTokenGenerator
+   */
+  protected $csrfToken;
+
+  /**
+   * Constructs a RouteProcessorCsrf object.
+   *
+   * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
+   *   The CSRF token generator.
+   */
+  function __construct(CsrfTokenGenerator $csrf_token) {
+    $this->csrfToken = $csrf_token;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processOutbound(Route $route, array &$parameters) {
+    if ($route->hasRequirement('_csrf')) {
+      // Adding this to the parameters means it will get merged into the query
+      // string when the route is compiled.
+      $parameters['csrf'] = $this->csrfToken->get($route->getRequirement('_csrf'));
+    }
+  }
+
+}
+
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index 9cd8f5a..8a525c9 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -14,6 +14,7 @@
 use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterRouteProcessorsPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteEnhancersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass;
@@ -63,6 +64,7 @@ public function register(ContainerBuilder $container) {
     $container->addCompilerPass(new RegisterServicesForDestructionPass());
     // Add the compiler pass that will process the tagged services.
     $container->addCompilerPass(new RegisterPathProcessorsPass());
+    $container->addCompilerPass(new RegisterRouteProcessorsPass());
     $container->addCompilerPass(new ListCacheBinsPass());
     // Add the compiler pass for appending string translators.
     $container->addCompilerPass(new RegisterStringTranslatorsPass());
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterRouteProcessorsPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterRouteProcessorsPass.php
new file mode 100644
index 0000000..647a725
--- /dev/null
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterRouteProcessorsPass.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\DependencyInjection\Compiler\RegisterRouteProcessorsPass.
+ */
+
+namespace Drupal\Core\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+/**
+ * Adds services to the route_processor_manager service.
+ */
+class RegisterRouteProcessorsPass implements CompilerPassInterface {
+
+  /**
+   * Adds services tagged 'route_processor_outbound' to route processor manager.
+   *
+   * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
+   *  The container to process.
+   */
+  public function process(ContainerBuilder $container) {
+    if (!$container->hasDefinition('route_processor_manager')) {
+      return;
+    }
+    $manager = $container->getDefinition('route_processor_manager');
+    // Add outbound route processors.
+    foreach ($container->findTaggedServiceIds('route_processor_outbound') as $id => $attributes) {
+      $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
+      $manager->addMethodCall('addOutbound', array(new Reference($id), $priority));
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
index 6f18839..e6f6eb8 100644
--- a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
@@ -9,6 +9,7 @@
 
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
@@ -40,6 +41,11 @@ public function __construct(AccessManager $access_manager) {
    */
   public function onKernelRequestAccessCheck(GetResponseEvent $event) {
     $request = $event->getRequest();
+
+    // The controller is being handled by the HTTP kernel, so add an attribute
+    // to tell us this is the controller request.
+    $request->attributes->set('_controller_request', TRUE);
+
     if (!$request->attributes->has(RouteObjectInterface::ROUTE_OBJECT)) {
       // If no Route is available it is likely a static resource and access is
       // handled elsewhere.
@@ -50,6 +56,8 @@ public function onKernelRequestAccessCheck(GetResponseEvent $event) {
     if (!$access) {
       throw new AccessDeniedHttpException();
     }
+
+    $request->attributes->remove('_controller_request');
   }
 
   /**
diff --git a/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php b/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php
index 347a877..9e69001 100644
--- a/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php
+++ b/core/lib/Drupal/Core/PathProcessor/OutboundPathProcessorInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\PathProcessor;
 
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
 
 /**
  * Defines an interface for classes that process the outbound path.
diff --git a/core/lib/Drupal/Core/RouteProcessor/OutboundRouteProcessorInterface.php b/core/lib/Drupal/Core/RouteProcessor/OutboundRouteProcessorInterface.php
new file mode 100644
index 0000000..8f421b3
--- /dev/null
+++ b/core/lib/Drupal/Core/RouteProcessor/OutboundRouteProcessorInterface.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\RouteProcessor\OutboundRouteProcessorInterface.
+ */
+
+namespace Drupal\Core\RouteProcessor;
+
+use Symfony\Component\Routing\Route;
+
+/**
+ * Defines an interface for classes that process the outbound route.
+ */
+interface OutboundRouteProcessorInterface {
+
+  /**
+   * Processes the outbound route.
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *   The outbound route to process.
+   *
+   * @param array $parameters
+   *   An array of parameters to be passed to the route compiler. Passed by
+   *   reference.
+   *
+   * @return
+   *   The processed path.
+   */
+  public function processOutbound(Route $route, array &$parameters);
+
+}
diff --git a/core/lib/Drupal/Core/RouteProcessor/RouteProcessorManager.php b/core/lib/Drupal/Core/RouteProcessor/RouteProcessorManager.php
new file mode 100644
index 0000000..42261cb
--- /dev/null
+++ b/core/lib/Drupal/Core/RouteProcessor/RouteProcessorManager.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\RouteProcessor\RouteProcessorManager.
+ */
+
+namespace Drupal\Core\RouteProcessor;
+
+use Symfony\Component\Routing\Route;
+
+/**
+ * Class RouteProcessorManager.
+ */
+class RouteProcessorManager implements OutboundRouteProcessorInterface {
+
+  /**
+   * Holds the array of outbound processors to cycle through.
+   *
+   * @var array
+   *   An array whose keys are priorities and whose values are arrays of path
+   *   processor objects.
+   */
+  protected $outboundProcessors = array();
+
+  /**
+   * Holds the array of outbound processors, sorted by priority.
+   *
+   * @var array
+   *   An array of path processor objects.
+   */
+  protected $sortedOutbound = array();
+
+  /**
+   * Adds an outbound processor object to the $outboundProcessors property.
+   *
+   * @param \Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface $processor
+   *   The processor object to add.
+   *
+   * @param int $priority
+   *   The priority of the processor being added.
+   */
+  public function addOutbound(OutboundRouteProcessorInterface $processor, $priority = 0) {
+    $this->outboundProcessors[$priority][] = $processor;
+    $this->sortedOutbound = array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processOutbound(Route $route, array &$parameters) {
+    $processors = $this->getOutbound();
+    foreach ($processors as $processor) {
+      $processor->processOutbound($route, $parameters);
+    }
+  }
+
+  /**
+   * Returns the sorted array of outbound processors.
+   *
+   * @return array
+   *   An array of processor objects.
+   */
+  protected function getOutbound() {
+    if (empty($this->sortedOutbound)) {
+      $this->sortedOutbound = $this->sortProcessors();
+    }
+
+    return $this->sortedOutbound;
+  }
+
+  /**
+   * Sorts the processors according to priority.
+   */
+  protected function sortProcessors() {
+    $sorted = array();
+    krsort($this->outboundProcessors);
+
+    foreach ($this->outboundProcessors as $processors) {
+      $sorted = array_merge($sorted, $processors);
+    }
+    return $sorted;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/NullGenerator.php b/core/lib/Drupal/Core/Routing/NullGenerator.php
index b6e2609..61e30bc 100644
--- a/core/lib/Drupal/Core/Routing/NullGenerator.php
+++ b/core/lib/Drupal/Core/Routing/NullGenerator.php
@@ -9,6 +9,7 @@
 
 use Symfony\Component\Routing\RequestContext;
 use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\Route;
 
 /**
  * No-op implementation of a Url Generator, needed for backward compatibility.
@@ -46,7 +47,7 @@ public function getContext() {
   /**
    * Overrides Drupal\Core\Routing\UrlGenerator::processPath().
    */
-  protected function processPath($path, &$options = array()) {
+  protected function processPath($path, &$options = array(), Route $route = NULL) {
     return $path;
   }
 }
diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index 540a580..95c776c 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -19,6 +19,7 @@
 use Drupal\Component\Utility\Url;
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
 
 /**
  * Generates URLs from route names and parameters.
@@ -40,6 +41,13 @@ class UrlGenerator extends ProviderBasedGenerator implements UrlGeneratorInterfa
   protected $pathProcessor;
 
   /**
+   * The route processor.
+   *
+   * @var \Drupal\Tests\Core\RouteProcessor\OutboundRouteProcessorInterface
+   */
+  protected $routeProcessor;
+
+  /**
    * The base path to use for urls.
    *
    * @var string
@@ -77,10 +85,11 @@ class UrlGenerator extends ProviderBasedGenerator implements UrlGeneratorInterfa
    * @param \Symfony\Component\HttpKernel\Log\LoggerInterface $logger
    *   An optional logger for recording errors.
    */
-  public function __construct(RouteProviderInterface $provider, OutboundPathProcessorInterface $path_processor, ConfigFactory $config, Settings $settings, LoggerInterface $logger = NULL) {
+  public function __construct(RouteProviderInterface $provider, OutboundPathProcessorInterface $path_processor, OutboundRouteProcessorInterface $route_processor, ConfigFactory $config, Settings $settings, LoggerInterface $logger = NULL) {
     parent::__construct($provider, $logger);
 
     $this->pathProcessor = $path_processor;
+    $this->routeProcessor = $route_processor;
     $this->mixedModeSessions = $settings->get('mixed_mode_sessions', FALSE);
     $allowed_protocols = $config->get('system.filter')->get('protocols') ?: array('http', 'https');
     Url::setAllowedProtocols($allowed_protocols);
@@ -167,10 +176,13 @@ public function generate($name, $parameters = array(), $absolute = FALSE) {
   public function generateFromRoute($name, $parameters = array(), $options = array()) {
     $absolute = !empty($options['absolute']);
     $route = $this->getRoute($name);
+    $this->processRoute($route, $parameters);
+
     // Symfony adds any parameters that are not path slugs as query strings.
     if (isset($options['query']) && is_array($options['query'])) {
       $parameters = (array) $parameters + $options['query'];
     }
+
     $path = $this->getInternalPathFromRoute($route, $parameters);
     $path = $this->processPath($path, $options);
     $fragment = '';
@@ -179,6 +191,7 @@ public function generateFromRoute($name, $parameters = array(), $options = array
         $fragment = '#' . $fragment;
       }
     }
+
     $base_url = $this->context->getBaseUrl();
     if (!$absolute || !$host = $this->context->getHost()) {
       return $base_url . $path . $fragment;
@@ -336,6 +349,19 @@ protected function processPath($path, &$options = array()) {
   }
 
   /**
+   * Passes the route to the processor manager for altering before complation.
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *   The route object to process.
+   *
+   * @param array $parameters
+   *   An array of parameters to be passed to the route compiler.
+   */
+  protected function processRoute(SymfonyRoute $route, array &$parameters) {
+    $this->routeProcessor->processOutbound($route, $parameters);
+  }
+
+  /**
    * Returns whether or not the url generator has been initialized.
    *
    * @return bool
diff --git a/core/tests/Drupal/Tests/Core/Access/CsrfAccessCheckTest.php b/core/tests/Drupal/Tests/Core/Access/CsrfAccessCheckTest.php
new file mode 100644
index 0000000..764797c
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Access/CsrfAccessCheckTest.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Access\CsrfAccessCheckTest.
+ */
+
+namespace Drupal\Tests\Core\Access;
+
+use Drupal\Core\Access\AccessCheckInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+use Drupal\Core\Access\CsrfAccessCheck;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the access checker which deals with drupal_valid_token.
+ *
+ * @see \Drupal\Core\Access\CsrfAccessCheck
+ */
+class CsrfAccessCheckTest extends UnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'CSRF access checker',
+      'description' => 'Tests CSRF access control for routes.',
+      'group' => 'Routing',
+    );
+  }
+
+  /**
+   * Tests CsrfAccessCheck::appliesTo().
+   */
+  public function testAppliesTo() {
+    $this->assertEquals($this->accessChecker->appliesTo(), array('_csrf'), 'Access checker returned the expected appliesTo() array.');
+  }
+
+  /**
+   * Tests CsrfAccessCheck::access().
+   */
+  public function testAccess() {
+    $token_value = 'b1rd';
+    $route = new Route('/foo', array(), array('_csrf' => $token_value));
+    $request = new Request(array(
+      'csrf' => drupal_get_token($token_value),
+    ));
+    $access_check = new CsrfAccessCheck();
+    $access = $access_check->access($route, $request);
+    $this->assertEquals(AccessCheckInterface::ALLOW, $access);
+
+    // Run the same request with an invalid token.
+    $route = new Route('/foo', array(), array('_csrf: ' . $token_value));
+    $request = new Request(array(
+      'csrf' => $token_value,
+    ));
+    $access_check = new CsrfAccessCheck();
+    $access = $access_check->access($route, $request);
+    $this->assertEquals(AccessCheckInterface::KILL, $access);
+  }
+
+}
