diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index f54e24c3ee9952cadf6be427683e74e07f5b3363..763853c1b460dcf3b85e044e314e5fd916a41edd 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -46,6 +46,10 @@ mapping:
   label: Mapping
   class: '\Drupal\Core\Config\Schema\Mapping'
   definition_class: '\Drupal\Core\TypedData\MapDataDefinition'
+  mapping: {}
+  constraints:
+    # By default, only allow the explicitly listed mapping keys.
+    ValidKeys: '<infer>'
 sequence:
   label: Sequence
   class: '\Drupal\Core\Config\Schema\Sequence'
diff --git a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php
index 91b0a5542589a94b4aec0d2c3e203acb62fb717a..8192f5689a79121f8e11d781833ddd0bd4667b5c 100644
--- a/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php
+++ b/core/modules/block/tests/src/Kernel/Migrate/d6/MigrateBlockTest.php
@@ -309,7 +309,7 @@ public function testBlockMigration() {
     // Check migrate messages.
     $messages = iterator_to_array($this->getMigration('d6_block')->getIdMap()->getMessages());
     $this->assertCount(2, $messages);
-    $this->assertSame($messages[1]->message, 'Schema errors for block.block.aggregator with the following errors: block.block.aggregator:settings.block_count missing schema, block.block.aggregator:settings.feed missing schema');
+    $this->assertSame($messages[1]->message, 'Schema errors for block.block.aggregator with the following errors: block.block.aggregator:settings.block_count missing schema, block.block.aggregator:settings.feed missing schema, 0 [settings] &#039;block_count&#039; is not a supported key., 1 [settings] &#039;feed&#039; is not a supported key.');
   }
 
 }
diff --git a/core/modules/config/tests/src/Functional/SchemaConfigListenerWebTest.php b/core/modules/config/tests/src/Functional/SchemaConfigListenerWebTest.php
index 3d78745758e8c0aff6566972a25e64d8bcebdc88..7701ff626880d98f3ac745b55fbf9b4a3b1d4681 100644
--- a/core/modules/config/tests/src/Functional/SchemaConfigListenerWebTest.php
+++ b/core/modules/config/tests/src/Functional/SchemaConfigListenerWebTest.php
@@ -55,7 +55,7 @@ public function testConfigSchemaChecker() {
       $this->fail('Expected SchemaIncompleteException thrown');
     }
     catch (SchemaIncompleteException $e) {
-      $this->assertEquals('Schema errors for config_test.types with the following errors: config_test.types:array variable type is integer but applied schema class is Drupal\Core\Config\Schema\Sequence, config_test.types:foo missing schema', $e->getMessage());
+      $this->assertEquals("Schema errors for config_test.types with the following errors: config_test.types:array variable type is integer but applied schema class is Drupal\Core\Config\Schema\Sequence, config_test.types:foo missing schema, 0 [] &#039;foo&#039; is not a supported key.", $e->getMessage());
     }
 
     // Test that the config event listener is working in the child site.
diff --git a/core/tests/Drupal/KernelTests/Config/TypedConfigTest.php b/core/tests/Drupal/KernelTests/Config/TypedConfigTest.php
index a9be4d4ce30d7c7ff48ab41778fb80cec2b55b3f..0b05694c08b48e659d3b5d391e3fc4a51c697cad 100644
--- a/core/tests/Drupal/KernelTests/Config/TypedConfigTest.php
+++ b/core/tests/Drupal/KernelTests/Config/TypedConfigTest.php
@@ -181,9 +181,20 @@ public function testSimpleConfigValidation() {
     $value['zebra'] = 'foo';
     $typed_config->setValue($value);
     $result = $typed_config->validate();
-    $this->assertCount(1, $result);
-    $this->assertEquals('', $result->get(0)->getPropertyPath());
-    $this->assertEquals('Unexpected keys: elephant, zebra', $result->get(0)->getMessage());
+    $this->assertCount(3, $result);
+    // 2 constraint violations triggered by the default validation constraint
+    // for `type: mapping`
+    // @see \Drupal\Core\Validation\Plugin\Validation\Constraint\ValidKeysConstraint
+    $this->assertSame('', $result->get(0)->getPropertyPath());
+    $this->assertEquals("'elephant' is not a supported key.", $result->get(0)->getMessage());
+    $this->assertSame('', $result->get(1)->getPropertyPath());
+    $this->assertEquals("'zebra' is not a supported key.", $result->get(1)->getMessage());
+    // 1 additional constraint violation triggered by the custom
+    // constraint for the `config_test.validation` type, which indirectly
+    // extends `type: mapping` (via `type: config_object`).
+    // @see \Drupal\config_test\ConfigValidation::validateMapping()
+    $this->assertEquals('', $result->get(2)->getPropertyPath());
+    $this->assertEquals('Unexpected keys: elephant, zebra', $result->get(2)->getMessage());
   }
 
 }
diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
index f04e330a4205751bd4abffe36bc3331e7e9870a7..cff3bc0e246b404158773fb41937ea222548e880 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
@@ -75,6 +75,7 @@ public function testSchemaMapping() {
     $expected['type'] = 'config_schema_test.someschema';
     $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
     $expected['unwrap_for_canonical_representation'] = TRUE;
+    $expected['constraints'] = ['ValidKeys' => '<infer>'];
     $this->assertEquals($expected, $definition, 'Retrieved the right metadata for configuration with only some schema.');
 
     // Check type detection on elements with undefined types.
@@ -120,6 +121,7 @@ public function testSchemaMapping() {
     $expected['type'] = 'system.maintenance';
     $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
     $expected['unwrap_for_canonical_representation'] = TRUE;
+    $expected['constraints'] = ['ValidKeys' => '<infer>'];
     $this->assertEquals($expected, $definition, 'Retrieved the right metadata for system.maintenance');
 
     // Mixed schema with ignore elements.
@@ -150,6 +152,7 @@ public function testSchemaMapping() {
     ];
     $expected['type'] = 'config_schema_test.ignore';
     $expected['unwrap_for_canonical_representation'] = TRUE;
+    $expected['constraints'] = ['ValidKeys' => '<infer>'];
 
     $this->assertEquals($expected, $definition);
 
@@ -194,6 +197,7 @@ public function testSchemaMapping() {
     $expected['mapping']['third_party_settings']['sequence']['type'] = '[%parent.%parent.%type].third_party.[%key]';
     $expected['mapping']['_core']['type'] = '_core_config_info';
     $expected['type'] = 'image.style.*';
+    $expected['constraints'] = ['ValidKeys' => '<infer>'];
 
     $this->assertEquals($expected, $definition);
 
@@ -212,6 +216,7 @@ public function testSchemaMapping() {
     $expected['mapping']['upscale']['type'] = 'boolean';
     $expected['mapping']['upscale']['label'] = 'Upscale';
     $expected['type'] = 'image.effect.image_scale';
+    $expected['constraints'] = ['ValidKeys' => '<infer>'];
 
     $this->assertEquals($expected, $definition, 'Retrieved the right metadata for image.effect.image_scale');
 
@@ -235,6 +240,7 @@ public function testSchemaMapping() {
       'integer' => ['type' => 'integer'],
       'string' => ['type' => 'string'],
     ];
+    $expected['constraints'] = ['ValidKeys' => '<infer>'];
     $this->assertEquals($expected, $definition, 'Retrieved the right metadata for config_test.dynamic.third_party:third_party_settings.config_schema_test');
 
     // More complex, several level deep test.
@@ -252,6 +258,7 @@ public function testSchemaMapping() {
     $expected['type'] = 'config_schema_test.someschema.somemodule.*.*';
     $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
     $expected['unwrap_for_canonical_representation'] = TRUE;
+    $expected['constraints'] = ['ValidKeys' => '<infer>'];
 
     $this->assertEquals($expected, $definition, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection');
 
@@ -520,6 +527,7 @@ public function testSchemaFallback() {
     $expected['mapping']['testdescription']['type'] = 'text';
     $expected['mapping']['testdescription']['label'] = 'Description';
     $expected['type'] = 'config_schema_test.wildcard_fallback.*';
+    $expected['constraints'] = ['ValidKeys' => '<infer>'];
 
     $this->assertEquals($expected, $definition, 'Retrieved the right metadata for config_schema_test.wildcard_fallback.something');
 
diff --git a/core/tests/Drupal/KernelTests/Core/Config/SchemaCheckTraitTest.php b/core/tests/Drupal/KernelTests/Core/Config/SchemaCheckTraitTest.php
index bd89523a54df07e3999b76e9991cc56311cd30b2..5aee77231a81b6ffb96629b2c4e2e1693105adaa 100644
--- a/core/tests/Drupal/KernelTests/Core/Config/SchemaCheckTraitTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Config/SchemaCheckTraitTest.php
@@ -63,7 +63,9 @@ public function testTrait() {
       'config_test.types:boolean' => 'non-scalar value but not defined as an array (such as mapping or sequence)',
       // Validation constraints violations.
       // @see \Drupal\Core\TypedData\TypedDataInterface::validate()
-      '0' => '[boolean] This value should be of the correct primitive type.',
+      '0' => "[] 'new_key' is not a supported key.",
+      '1' => "[] 'new_array' is not a supported key.",
+      '2' => '[boolean] This value should be of the correct primitive type.',
     ];
     $this->assertEquals($expected, $ret);
   }
diff --git a/core/tests/Drupal/Tests/Traits/Core/Config/SchemaConfigListenerTestTrait.php b/core/tests/Drupal/Tests/Traits/Core/Config/SchemaConfigListenerTestTrait.php
index 2c66edb355cf91b3eff128dc43fbfff72523f983..8f792fb5954c5d67262b4811d73da4a91534c498 100644
--- a/core/tests/Drupal/Tests/Traits/Core/Config/SchemaConfigListenerTestTrait.php
+++ b/core/tests/Drupal/Tests/Traits/Core/Config/SchemaConfigListenerTestTrait.php
@@ -55,7 +55,7 @@ public function testConfigSchemaChecker() {
       $this->fail($message);
     }
     catch (SchemaIncompleteException $e) {
-      $this->assertEquals('Schema errors for config_test.types with the following errors: config_test.types:array variable type is integer but applied schema class is Drupal\Core\Config\Schema\Sequence, config_test.types:foo missing schema', $e->getMessage());
+      $this->assertEquals("Schema errors for config_test.types with the following errors: config_test.types:array variable type is integer but applied schema class is Drupal\Core\Config\Schema\Sequence, config_test.types:foo missing schema, 0 [] &#039;foo&#039; is not a supported key.", $e->getMessage());
     }
 
   }