diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index d6fa16c..d198c84 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -16,6 +16,8 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\filter\FilterFormatInterface;
 use Drupal\filter\Plugin\FilterInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\file\FileInterface;
 
 /**
  * Implements hook_help().
@@ -468,6 +470,131 @@ function _editor_delete_file_usage(array $uuids, EntityInterface $entity, $count
 }
 
 /**
+ * Implements hook_file_download().
+ *
+ * @see file_file_download()
+ * @see file_get_file_references()
+ */
+function editor_file_download($uri) {
+  // Get the file record based on the URI. If not in the database just return.
+  /** @var \Drupal\file\FileInterface[] $files */
+  $files = entity_load_multiple_by_properties('file', array('uri' => $uri));
+  if (count($files)) {
+    foreach ($files as $item) {
+      // Since some database servers sometimes use a case-insensitive comparison
+      // by default, double check that the filename is an exact match.
+      if ($item->getFileUri() === $uri) {
+        $file = $item;
+        break;
+      }
+    }
+  }
+  if (!isset($file)) {
+    return;
+  }
+
+  // Temporary files are handled by file_file_download(), so nothing to do here
+  // about them.
+
+  // Find out which (if any) fields of this type contain the file.
+  $references = editor_get_file_references($file, EntityStorageInterface::FIELD_LOAD_CURRENT);
+
+  // Stop processing if there are no references in order to avoid returning
+  // headers for files controlled by other modules. Make an exception for
+  // temporary files where the host entity has not yet been saved (for example,
+  // an image preview on a node/add form) in which case, allow download by the
+  // file's owner.
+  if (empty($references) && ($file->isPermanent() || $file->getOwnerId() != \Drupal::currentUser()->id())) {
+    return;
+  }
+
+  // Editor.module MUST NOT call $file->access() here (like file_file_download()
+  // does) as checking the 'download' access to a file entity would end up in
+  // FileAccessControlHandler->checkAccess() and ->getFileReferences(), which
+  // calls file_get_file_references() again. This latter one would allow
+  // downloading files only handled by the file.module, which is exactly not the
+  // case right here.
+//  if (!($return = $file->access('download', NULL, TRUE))) {
+//    return -1;
+//  }
+
+  // Access is granted.
+  $headers = file_get_content_headers($file);
+  return $headers;
+}
+
+/**
+ * Retrieves a list of references to a file uploaded via EditorImageDialog.
+ *
+ * @param \Drupal\file\FileInterface $file
+ *   A file entity.
+ * @param int $age
+ *   (optional) A constant that specifies which references to count. Use
+ *   EntityStorageInterface::FIELD_LOAD_REVISION (the default) to retrieve all
+ *   references within all revisions or
+ *   EntityStorageInterface::FIELD_LOAD_CURRENT to retrieve references only in
+ *   the current revisions of all entities that have references to this file.
+ *
+ * @return array
+ *   A multidimensional array. The keys are field_name, entity_type,
+ *   entity_id and the value is an entity referencing this file.
+ *
+ * @ingroup file
+ * @see file_get_file_references()
+ */
+function editor_get_file_references(FileInterface $file, $age = EntityStorageInterface::FIELD_LOAD_REVISION) {
+  $references = &drupal_static(__FUNCTION__, array());
+
+  // Fill the static cache, disregard $field and $field_type for now.
+  if (!isset($references[$file->id()][$age])) {
+    $references[$file->id()][$age] = array();
+    $usage_list = \Drupal::service('file.usage')->listUsage($file);
+    $file_usage_list = isset($usage_list['editor']) ? $usage_list['editor'] : array();
+    foreach ($file_usage_list as $entity_type_id => $entity_ids) {
+      $entities = entity_load_multiple($entity_type_id, array_keys($entity_ids));
+      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+      foreach ($entities as $entity) {
+        $bundle = $entity->bundle();
+
+        // We need to find editor fields for this entity type and bundle.
+        if (!isset($editor_fields[$entity_type_id][$bundle])) {
+          $editor_fields[$entity_type_id][$bundle] = _editor_get_formatted_text_fields($entity);
+        }
+        foreach ($editor_fields[$entity_type_id][$bundle] as $field_name) {
+          // Iterate over the field items to find the referenced file and field
+          // name. This will fail if the usage checked is in a non-current
+          // revision because field items are from the current
+          // revision.
+          // We also iterate over all translations because a file can be linked
+          // to a language other than the default.
+          foreach ($entity->getTranslationLanguages() as $langcode => $language) {
+            /** @var \Drupal\text\Plugin\Field\FieldType\TextItemBase $item */
+            foreach ($entity->getTranslation($langcode)->get($field_name) as $item) {
+              // Check if the file is referenced in either
+              // $item->values['value'] or $item->values['summary'] (if the
+              // latter exists at all).
+              $value = $item->getValue();
+              foreach (['summary', 'value'] as $subfield) {
+                if (isset($value[$subfield])) {
+                  $xpath = new \DOMXPath(Html::load($value['value']));
+                  /** @var DOMNodeList $query */
+                  $query = $xpath->query('//*[@data-entity-type="file" and @data-entity-uuid="' . $file->uuid() . '"]');
+                  if ($query->length) {
+                    $references[$file->id()][$age][$field_name][$entity_type_id][$entity->id()] = $entity;
+                    break;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  return $references[$file->id()][$age];
+}
+
+/**
  * Finds all files referenced (data-entity-uuid) by formatted text fields.
  *
  * @param EntityInterface $entity
diff --git a/core/modules/editor/src/Tests/EditorPrivateFileReferenceFilterTest.php b/core/modules/editor/src/Tests/EditorPrivateFileReferenceFilterTest.php
new file mode 100644
index 0000000..872cec9
--- /dev/null
+++ b/core/modules/editor/src/Tests/EditorPrivateFileReferenceFilterTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\editor\Tests;
+
+use Drupal\file\Entity\File;
+use Drupal\file\FileInterface;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests Editor module's file reference filter with private files.
+ *
+ * @group editor
+ */
+class EditorPrivateFileReferenceFilterTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    // Needed for the config: this is the only module in core that utilizes the
+    // functionality in editor.module to be tested, and depends on that.
+    'ckeditor',
+    // Depends on filter.module (indirectly).
+    'node',
+    // Pulls in the config we're using during testing which create a text format
+    // - with the filter_html_image_secure filter DISABLED,
+    // - with the editor set to CKEditor,
+    // - with drupalimage.image_upload.scheme set to 'private',
+    // - with drupalimage.image_upload.directory set to ''.
+    'editor_test',
+  ];
+
+  /**
+   * Tests the editor file reference filter with private files.
+   */
+  function testEditorPrivateFileReferenceFilter() {
+    // Create a content type with a body field.
+    $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+
+    // Create a file in the 'private:// ' stream.
+    $filename = 'test.png';
+    $src = '/system/files/' . $filename;
+    /** @var FileInterface $file */
+    $file = File::create([
+      'uri' => 'private://' . $filename,
+      'status' => FILE_STATUS_PERMANENT,
+    ]);
+    // Create the file itself.
+    file_put_contents($file->getFileUri(), $this->randomString());
+    $file->save();
+
+    // Create a node with its body field properly pointing to the just-created
+    // file.
+    $node = $this->drupalCreateNode([
+      'type' => 'page',
+      'body' => [
+        'value' => '<img alt="alt" data-entity-type="file" data-entity-uuid="' . $file->uuid() . '" src="' . $src . '" />',
+        'format' => 'private_images',
+      ],
+    ]);
+    $this->drupalGet('/node/' . $node->id());
+
+    // Do the actual test. The image should be visible for anonymous.
+    $this->drupalGet($src);
+    $this->assertResponse(200, 'Image is downloadable as anonymous.');
+  }
+
+}
diff --git a/core/modules/editor/tests/modules/config/install/editor.editor.private_images.yml b/core/modules/editor/tests/modules/config/install/editor.editor.private_images.yml
new file mode 100644
index 0000000..2de126a
--- /dev/null
+++ b/core/modules/editor/tests/modules/config/install/editor.editor.private_images.yml
@@ -0,0 +1,34 @@
+format: private_images
+status: true
+langcode: en
+editor: ckeditor
+settings:
+  toolbar:
+    rows:
+      -
+        -
+          name: Media
+          items:
+            - DrupalImage
+        -
+          name: Tools
+          items:
+            - Source
+  plugins:
+    language:
+      language_list: un
+    stylescombo:
+      styles: ''
+image_upload:
+  status: true
+  scheme: private
+  directory: ''
+  max_size: ''
+  max_dimensions:
+    width: null
+    height: null
+dependencies:
+  config:
+    - filter.format.private_images
+  module:
+    - ckeditor
diff --git a/core/modules/editor/tests/modules/config/install/filter.format.private_images.yml b/core/modules/editor/tests/modules/config/install/filter.format.private_images.yml
new file mode 100644
index 0000000..261bd90
--- /dev/null
+++ b/core/modules/editor/tests/modules/config/install/filter.format.private_images.yml
@@ -0,0 +1,23 @@
+format: private_images
+name: 'Private images'
+status: true
+langcode: en
+filters:
+  editor_file_reference:
+    id: editor_file_reference
+    provider: editor
+    status: true
+    weight: 0
+    settings: {  }
+  filter_html:
+    id: filter_html
+    provider: filter
+    status: false
+    weight: -10
+    settings:
+      allowed_html: '<img src alt data-entity-type data-entity-uuid>'
+      filter_html_help: true
+      filter_html_nofollow: false
+dependencies:
+  module:
+    - editor
diff --git a/core/modules/editor/tests/modules/editor_test.info.yml b/core/modules/editor/tests/modules/editor_test.info.yml
index 40b6ff0..3c8d511 100644
--- a/core/modules/editor/tests/modules/editor_test.info.yml
+++ b/core/modules/editor/tests/modules/editor_test.info.yml
@@ -4,3 +4,5 @@ description: 'Support module for the Text Editor module tests.'
 core: 8.x
 package: Testing
 version: VERSION
+dependencies:
+  - filter
