diff --git a/core/modules/content_moderation/content_moderation.module b/core/modules/content_moderation/content_moderation.module
index 582242b..8f79932 100644
--- a/core/modules/content_moderation/content_moderation.module
+++ b/core/modules/content_moderation/content_moderation.module
@@ -8,16 +8,20 @@
 use Drupal\content_moderation\EntityOperations;
 use Drupal\content_moderation\EntityTypeInfo;
 use Drupal\content_moderation\ContentPreprocess;
+use Drupal\content_moderation\FileAccessControlHandler;
 use Drupal\content_moderation\Plugin\Action\ModerationOptOutPublishNode;
 use Drupal\content_moderation\Plugin\Action\ModerationOptOutUnpublishNode;
 use Drupal\content_moderation\Plugin\Menu\EditTab;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\file\FileInterface;
 use Drupal\node\NodeInterface;
 use Drupal\node\Plugin\Action\PublishNode;
 use Drupal\node\Plugin\Action\UnpublishNode;
@@ -61,6 +65,10 @@ function content_moderation_entity_type_alter(array &$entity_types) {
   \Drupal::service('class_resolver')
     ->getInstanceFromDefinition(EntityTypeInfo::class)
     ->entityTypeAlter($entity_types);
+
+  if (isset($entity_types['file'])) {
+    $entity_types['file']->setAccessClass(FileAccessControlHandler::class);
+  }
 }
 
 /**
@@ -222,3 +230,147 @@ function content_moderation_action_info_alter(&$definitions) {
     $definitions['node_unpublish_action']['class'] = ModerationOptOutUnpublishNode::class;
   }
 }
+
+/**
+ * Implements hook_file_download().
+ */
+function content_moderation_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;
+  }
+
+  // Find out which (if any) fields of this type contain the file.
+  $references = content_moderation_file_get_file_references($file, NULL, EntityStorageInterface::FIELD_LOAD_CURRENT, NULL);
+
+  // 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;
+  }
+
+  if (!$file->access('download')) {
+    return -1;
+  }
+
+  // Access is granted.
+  $headers = file_get_content_headers($file);
+  return $headers;
+}
+
+/**
+ * Retrieves a list of references to a file.
+ *
+ * @param \Drupal\file\FileInterface $file
+ *   A file entity.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface|null $field
+ *   (optional) A field definition to be used for this check. If given,
+ *   limits the reference check to the given field. Defaults to NULL.
+ * @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.
+ * @param string $field_type
+ *   (optional) The name of a field type. If given, limits the reference check
+ *   to fields of the given type. If both $field and $field_type are given but
+ *   $field is not the same type as $field_type, an empty array will be
+ *   returned. Defaults to '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
+ */
+function content_moderation_file_get_file_references(FileInterface $file, FieldDefinitionInterface $field = NULL, $age = EntityStorageInterface::FIELD_LOAD_REVISION, $field_type = 'file') {
+  $references = &drupal_static(__FUNCTION__, array());
+  $field_columns = &drupal_static(__FUNCTION__ . ':field_columns', 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['file']) ? $usage_list['file'] : array();
+    foreach ($file_usage_list as $entity_type_id => $entity_ids) {
+      $entity_type = \Drupal::entityTypeManager()->getDefinition($entity_type_id);
+      $results = \Drupal::entityQuery($entity_type_id)
+        ->condition($entity_type->getKey('id'), array_keys($entity_ids), 'IN')
+        ->allRevisions()
+        ->execute();
+
+      $entities = [];
+      $storage = \Drupal::entityTypeManager()->getStorage($entity_type_id);
+      foreach ($results as $revision_id => $entity_id) {
+        $entities[] = $storage->loadRevision($revision_id);
+      }
+
+      foreach ($entities as $entity) {
+        $bundle = $entity->bundle();
+        // We need to find file fields for this entity type and bundle.
+        if (!isset($file_fields[$entity_type_id][$bundle])) {
+          $file_fields[$entity_type_id][$bundle] = array();
+          // This contains the possible field names.
+          foreach ($entity->getFieldDefinitions() as $field_name => $field_definition) {
+            // If this is the first time this field type is seen, check
+            // whether it references files.
+            if (!isset($field_columns[$field_definition->getType()])) {
+              $field_columns[$field_definition->getType()] = file_field_find_file_reference_column($field_definition);
+            }
+            // If the field type does reference files then record it.
+            if ($field_columns[$field_definition->getType()]) {
+              $file_fields[$entity_type_id][$bundle][$field_name] = $field_columns[$field_definition->getType()];
+            }
+          }
+        }
+        foreach ($file_fields[$entity_type_id][$bundle] as $field_name => $field_column) {
+          // 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) {
+            foreach ($entity->getTranslation($langcode)->get($field_name) as $item) {
+              if ($file->id() == $item->{$field_column}) {
+                $references[$file->id()][$age][$field_name][$entity_type_id][$entity->id()] = $entity;
+                break;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  $return = $references[$file->id()][$age];
+  // Filter the static cache down to the requested entries. The usual static
+  // cache is very small so this will be very fast.
+  if ($field || $field_type) {
+    foreach ($return as $field_name => $data) {
+      foreach (array_keys($data) as $entity_type_id) {
+        $field_storage_definitions = \Drupal::entityManager()->getFieldStorageDefinitions($entity_type_id);
+        $current_field = $field_storage_definitions[$field_name];
+        if (($field_type && $current_field->getType() != $field_type) || ($field && $field->uuid() != $current_field->uuid())) {
+          unset($return[$field_name][$entity_type_id]);
+        }
+      }
+    }
+  }
+  return $return;
+}
diff --git a/core/modules/content_moderation/src/FileAccessControlHandler.php b/core/modules/content_moderation/src/FileAccessControlHandler.php
new file mode 100644
index 0000000..4255138
--- /dev/null
+++ b/core/modules/content_moderation/src/FileAccessControlHandler.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Drupal\content_moderation;
+
+use Drupal\file\FileAccessControlHandler as OriginalFileAccessControlHandler;
+use Drupal\file\FileInterface;
+
+/**
+ * Overrides Files access control handler to handle revisions in references.
+ */
+class FileAccessControlHandler extends OriginalFileAccessControlHandler {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getFileReferences(FileInterface $file) {
+    return content_moderation_file_get_file_references($file);
+  }
+
+}
