diff --git a/jsonapi.services.yml b/jsonapi.services.yml
index 55bd5ce..946ea6c 100644
--- a/jsonapi.services.yml
+++ b/jsonapi.services.yml
@@ -66,3 +66,7 @@ services:
   jsonapi.field_resolver:
     class: Drupal\jsonapi\Context\FieldResolver
     arguments: ['@jsonapi.current_context', '@entity_field.manager']
+  access_check.jsonapi.custom_parameter_names:
+    class: Drupal\jsonapi\Access\CustomParameterNames
+    tags:
+      - { name: access_check, applies_to: _custom_parameter_names }
diff --git a/src/Access/CustomParameterNames.php b/src/Access/CustomParameterNames.php
new file mode 100644
index 0000000..8f2dfe9
--- /dev/null
+++ b/src/Access/CustomParameterNames.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Drupal\jsonapi\Access;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Routing\Access\AccessInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Validates custom parameter names.
+ */
+class CustomParameterNames implements AccessInterface {
+
+  /**
+   * Validates the JSONAPI parameter names.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return \Drupal\Core\Access\AccessResult
+   *   The access result.
+   */
+  public function access(Request $request) {
+    $json_api_params = $request->attributes->get('_json_api_params', []);
+    if (!$this->validate($json_api_params)) {
+      return AccessResult::forbidden();
+    }
+    return AccessResult::allowed();
+  }
+
+  /**
+   * Validates the JSONAPI parameters.
+   *
+   * @param string[] $json_api_params
+   *   The JSONAPI parameters.
+   *
+   * @return bool
+   */
+  protected function validate(array $json_api_params) {
+    $valid = TRUE;
+
+    foreach (array_keys($json_api_params) as $name) {
+      if (strpbrk($name, '+,.[]!”#$%&’()*/:;<=>?@^`{}~|')) {
+        $valid = FALSE;
+        break;
+      }
+
+      if (strpbrk($name[0], '-_ ') || strpbrk($name[strlen($name) - 1], '-_ ')) {
+        $valid = FALSE;
+        break;
+      }
+    }
+
+    return $valid;
+  }
+
+}
diff --git a/src/Routing/Routes.php b/src/Routing/Routes.php
index d3f27ac..312f114 100644
--- a/src/Routing/Routes.php
+++ b/src/Routing/Routes.php
@@ -96,6 +96,7 @@ class Routes implements ContainerInjectionInterface {
         ->setRequirement('_bundle', $bundle)
         ->setRequirement('_permission', 'access content')
         ->setRequirement('_format', 'api_json')
+        ->setRequirement('_custom_parameter_names', 'TRUE')
         ->setOption('_auth', $this->authProviderList())
         ->setOption('serialization_class', DocumentWrapperInterface::class)
         ->setMethods(['GET', 'POST']));
@@ -108,6 +109,7 @@ class Routes implements ContainerInjectionInterface {
         ->setRequirement('_bundle', $bundle)
         ->setRequirement('_permission', 'access content')
         ->setRequirement('_format', 'api_json')
+        ->setRequirement('_custom_parameter_names', 'TRUE')
         ->setOption('parameters', $parameters)
         ->setOption('_auth', $this->authProviderList())
         ->setOption('serialization_class', DocumentWrapperInterface::class)
@@ -120,6 +122,7 @@ class Routes implements ContainerInjectionInterface {
         ->setRequirement('_bundle', $bundle)
         ->setRequirement('_permission', 'access content')
         ->setRequirement('_format', 'api_json')
+        ->setRequirement('_custom_parameter_names', 'TRUE')
         ->setOption('parameters', $parameters)
         ->setOption('_auth', $this->authProviderList())
         ->setMethods(['GET']));
@@ -131,6 +134,7 @@ class Routes implements ContainerInjectionInterface {
         ->setRequirement('_bundle', $bundle)
         ->setRequirement('_permission', 'access content')
         ->setRequirement('_format', 'api_json')
+        ->setRequirement('_custom_parameter_names', 'TRUE')
         ->setOption('parameters', $parameters)
         ->setOption('_auth', $this->authProviderList())
         ->setOption('serialization_class', EntityReferenceFieldItemList::class)
diff --git a/tests/src/Unit/Access/CustomParameterNamesTest.php b/tests/src/Unit/Access/CustomParameterNamesTest.php
new file mode 100644
index 0000000..632b402
--- /dev/null
+++ b/tests/src/Unit/Access/CustomParameterNamesTest.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Drupal\Tests\jsonapi\Unit\Access;
+
+use Drupal\jsonapi\Access\CustomParameterNames;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * @coversDefaultClass \Drupal\jsonapi\Access\CustomParameterNames
+ * @group jsonapi
+ */
+class CustomParameterNamesTest extends \PHPUnit_Framework_TestCase {
+
+  /**
+   * @dataProvider providerTestJsonApiParamsValidation
+   * @covers ::access
+   * @covers ::validate
+   */
+  public function testJsonApiParamsValidation($name, $valid) {
+    $access_checker = new CustomParameterNames();
+
+    $request = new Request();
+    $request->attributes->set('_json_api_params', [$name => '123']);
+    $result = $access_checker->access($request);
+
+    if ($valid) {
+      $this->assertTrue($result->isAllowed());
+    }
+    else {
+      $this->assertFalse($result->isAllowed());
+    }
+  }
+
+  public function providerTestJsonApiParamsValidation() {
+    // Copied from http://jsonapi.org/format/upcoming/#document-member-names.
+    $data = [];
+    $data['alphanumeric-lowercase'] = ['12kittens', TRUE];
+    $data['alphanumeric-uppercase'] = ['12KITTENS', TRUE];
+    $data['alphanumeric-mixed'] = ['12KiTtEnS', TRUE];
+    $data['unicode-above-u+0080'] = ['12🐱🐱', TRUE];
+    $data['hyphen-start'] = ['-kittens', FALSE];
+    $data['hyphen-middle'] = ['kitt-ens', TRUE];
+    $data['hyphen-end'] = ['kittens-', FALSE];
+    $data['lowline-start'] = ['_kittens', FALSE];
+    $data['lowline-middle'] = ['kitt_ens', TRUE];
+    $data['lowline-end'] = ['kittens_', FALSE];
+    $data['space-start'] = [' kittens', FALSE];
+    $data['space-middle'] = ['kitt ens', TRUE];
+    $data['space-end'] = ['kittens ', FALSE];
+
+    $unsafe_chars = [
+      '+',
+      ',',
+      '.',
+      '[',
+      ']',
+      '!',
+      '”',
+      '#',
+      '$',
+      '%',
+      '&',
+      '’',
+      '(',
+      ')',
+      '*',
+      '/',
+      ':',
+      ';',
+      '<',
+      '=',
+      '>',
+      '?',
+      '@',
+      '“',
+      '^',
+      '`',
+      '{',
+      '|',
+      '}',
+      '~',
+    ];
+    foreach ($unsafe_chars as $unsafe_char) {
+      $data['unsafe-' . $unsafe_char] = ['kitt' . $unsafe_char . 'ens', FALSE];
+    }
+
+    return $data;
+  }
+
+}
