diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php
new file mode 100644
index 0000000..40945c0
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/PasswordItem.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem.
+ */
+
+namespace Drupal\Core\Field\Plugin\Field\FieldType;
+
+/**
+ * Defines the 'password' entity field type.
+ *
+ * @FieldType(
+ *   id = "password",
+ *   label = @Translation("Password"),
+ *   description = @Translation("An entity field containing a password value."),
+ *   no_ui = TRUE,
+ * )
+ */
+class PasswordItem extends StringItem {}
diff --git a/core/modules/serialization/serialization.services.yml b/core/modules/serialization/serialization.services.yml
index f32fac0..4c9397a 100644
--- a/core/modules/serialization/serialization.services.yml
+++ b/core/modules/serialization/serialization.services.yml
@@ -25,6 +25,11 @@ services:
     class: Drupal\serialization\Normalizer\ListNormalizer
     tags:
       - { name: normalizer }
+  serializer.normalizer.password_field_item:
+      class: Drupal\serialization\Normalizer\NullNormalizer
+      arguments: ['Drupal\Core\Field\Plugin\Field\FieldType\PasswordItem']
+      tags:
+        - { name: normalizer, priority: 20 }
   serializer.normalizer.typed_data:
     class: Drupal\serialization\Normalizer\TypedDataNormalizer
     tags:
diff --git a/core/modules/serialization/src/Normalizer/NullNormalizer.php b/core/modules/serialization/src/Normalizer/NullNormalizer.php
new file mode 100644
index 0000000..eec36d9
--- /dev/null
+++ b/core/modules/serialization/src/Normalizer/NullNormalizer.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\serialization\Normalizer\NullNormalizer.
+ */
+
+namespace Drupal\serialization\Normalizer;
+
+/**
+ * Null normalizer.
+ */
+class NullNormalizer extends NormalizerBase {
+
+  /**
+   * Constructs a NullNormalizer object.
+   *
+   * @param string|array $supported_interface_of_class
+   *   The supported interface(s) or class(es).
+   */
+  public function __construct($supported_interface_of_class) {
+    $this->supportedInterfaceOrClass = $supported_interface_of_class;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function normalize($object, $format = NULL, array $context = array()) {
+    return NULL;
+  }
+
+}
diff --git a/core/modules/serialization/src/Tests/EntitySerializationTest.php b/core/modules/serialization/src/Tests/EntitySerializationTest.php
index d45c2a6..48998ad 100644
--- a/core/modules/serialization/src/Tests/EntitySerializationTest.php
+++ b/core/modules/serialization/src/Tests/EntitySerializationTest.php
@@ -19,6 +19,13 @@
 class EntitySerializationTest extends NormalizerTestBase {
 
   /**
+   * Modules to install.
+   *
+   * @var array
+   */
+  public static $modules = array('serialization', 'system', 'field', 'entity_test', 'text', 'filter', 'user', 'entity_serialization_test');
+
+  /**
    * The test values.
    *
    * @var array
@@ -108,17 +115,33 @@ public function testNormalize() {
       $this->assertEqual($expected[$fieldName], $normalized[$fieldName], "ComplexDataNormalizer produces expected array for $fieldName.");
     }
     $this->assertEqual(array_diff_key($normalized, $expected), array(), 'No unexpected data is added to the normalized array.');
+  }
 
-    // Test password isn't available.
+  /**
+   * Tests user normalization, using the entity_serialization_test module to
+   * override some default access controls.
+   */
+  public function testUserNormalize() {
     $account = User::create([
-      'name' => 'foo',
+      'name' => 'serialization_test_user',
       'mail' => 'foo@example.com',
       'pass' => '123456',
     ]);
     $account->save();
+
+    // Test password isn't available.
     $normalized = $this->serializer->normalize($account);
-    $this->assertTrue(empty($normalized['pass']));
-    $this->assertTrue(empty($normalized['mail']));
+
+    $this->assertFalse(array_key_exists('pass', $normalized), '"pass" key does not exist in normalized user');
+    $this->assertFalse(array_key_exists('mail', $normalized), '"mail" key does not exist in normalized user');
+
+    // Test again using our test user, so that our access control override will
+    // allow password viewing.
+    $normalized = $this->serializer->normalize($account, NULL, ['account' => $account]);
+
+    // The key 'pass' will now exist, but the password value should be
+    // normalized to NULL.
+    $this->assertIdentical($normalized['pass'], [NULL], '"pass" value is normalized to [NULL]');
   }
 
   /**
diff --git a/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.info.yml b/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.info.yml
new file mode 100644
index 0000000..451a374
--- /dev/null
+++ b/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Entity serialization test support'
+type: module
+description: 'Provides test support for entity serialization tests.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.module b/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.module
new file mode 100644
index 0000000..a75e21c
--- /dev/null
+++ b/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.module
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Test support module for entity serialization tests.
+ */
+
+use Drupal\Core\Access\AccessResult;
+
+/**
+ * Implements hook_entity_field_access_alter().
+ *
+ * Overrides some default access control to support testing.
+ *
+ * @see Drupal\serialization\Tests\EntitySerializationTest::testUserNormalize()
+ */
+function entity_serialization_test_entity_field_access_alter(array &$grants, array $context) {
+  // Override default access control from UserAccessControlHandler to allow
+  // access to 'pass' field for the test user.
+  if ($context['field_definition']->getName() == 'pass' && $context['account']->getUsername() == 'serialization_test_user') {
+    $grants[':default'] = AccessResult::allowed()->inheritCacheability($grants[':default'])->cacheUntilEntityChanges($context['items']->getEntity());
+  }
+}
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/NullNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/NullNormalizerTest.php
new file mode 100644
index 0000000..e79c07f
--- /dev/null
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/NullNormalizerTest.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\serialization\Unit\Normalizer\NullNormalizerTest
+ */
+
+namespace Drupal\Tests\serialization\Unit\Normalizer;
+
+use Drupal\serialization\Normalizer\NullNormalizer;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\serialization\Normalizer\NullNormalizer
+ * @group serialization
+ */
+class NullNormalizerTest extends UnitTestCase {
+
+  /**
+   * The NullNormalizer instance.
+   *
+   * @var \Drupal\serialization\Normalizer\NullNormalizer
+   */
+  protected $normalizer;
+
+  /**
+   * The interface to use in testing.
+   *
+   * @var string
+   */
+  protected $interface = 'Drupal\Core\TypedData\TypedDataInterface';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->normalizer = new NullNormalizer($this->interface);
+  }
+
+  /**
+   * @covers ::__construct
+   * @covers ::supportsNormalization
+   */
+  public function testSupportsNormalization() {
+    $mock = $this->getMock('Drupal\Core\TypedData\TypedDataInterface');
+    $this->assertTrue($this->normalizer->supportsNormalization($mock));
+    // Also test that an object not implementing TypedDataInterface fails.
+    $this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
+  }
+
+  /**
+   * @covers ::normalize
+   */
+  public function testNormalize() {
+    $mock = $this->getMock('Drupal\Core\TypedData\TypedDataInterface');
+    $this->assertNull($this->normalizer->normalize($mock));
+  }
+
+}
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 22303e5..359edd1 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -482,7 +482,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
         'UserNameUnique' => array(),
       ));
 
-    $fields['pass'] = BaseFieldDefinition::create('string')
+    $fields['pass'] = BaseFieldDefinition::create('password')
       ->setLabel(t('Password'))
       ->setDescription(t('The password of this user (hashed).'));
 
