diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 93fed7a4b4..dc816e8c87 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -1008,15 +1008,17 @@ function _file_save_upload_single(\SplFileInfo $file_info, $form_field_name, $va
   // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
   // evaluates to TRUE.
   if (!\Drupal::config('system.file')->get('allow_insecure_uploads') && preg_match(FILE_INSECURE_EXTENSION_REGEX, $file->getFilename()) && (substr($file->getFilename(), -4) != '.txt')) {
-    $file->setMimeType('text/plain');
-    // The destination filename will also later be used to create the URI.
-    $file->setFilename($file->getFilename() . '.txt');
-    // The .txt extension may not be in the allowed list of extensions. We have
-    // to add it here or else the file upload will fail.
-    if (!empty($extensions)) {
-      $validators['file_validate_extensions'][0] .= ' txt';
+    // Rename the file if possible otherwise we have to fail.
+    if ($extensions === '' || preg_match('/(^|\s)txt(\s|$)/', $extensions)) {
+      $file->setMimeType('text/plain');
+      // The destination filename will also later be used to create the URI.
+      $file->setFilename($file->getFilename() . '.txt');
       \Drupal::messenger()->addStatus(t('For security reasons, your upload has been renamed to %filename.', ['%filename' => $file->getFilename()]));
     }
+    else {
+      \Drupal::messenger()->addError(t('The file %filename could not be uploaded because the extension is insecure.', ['%filename' => $file->getFilename()]));
+      return FALSE;
+    }
   }
 
   // If the destination is not provided, use the temporary directory.
diff --git a/core/modules/file/file.post_update.php b/core/modules/file/file.post_update.php
new file mode 100644
index 0000000000..919aee0880
--- /dev/null
+++ b/core/modules/file/file.post_update.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Post update functions for File.
+ */
+
+use Drupal\Core\Config\Entity\ConfigEntityUpdater;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\file\Plugin\Field\FieldType\FileItem;
+
+/**
+ * Adds txt to allowed extensions for all fields that allow uploads of insecure files.
+ */
+function file_post_update_update_allowed_file_extensions_if_insecure(&$sandbox = NULL) {
+  if (\Drupal::config('system.file')->get('allow_insecure_uploads')) {
+    return t('The system is configured to allow insecure file uploads. No file field updates are necessary.');
+  }
+
+  $updater = function (FieldConfig $field) {
+    // Determine if this is field uses a item definition that extends FileItem.
+    if (is_subclass_of($field->getItemDefinition()->getClass(), FileItem::class)) {
+      $allowed_extensions_string = $field->getSetting('file_extensions');
+      $allowed_extensions = array_filter(explode(' ', $allowed_extensions_string));
+      if (in_array('txt', $allowed_extensions, TRUE)) {
+        // .txt is specifically allowed there's nothing to do.
+        return FALSE;
+      }
+      foreach ($allowed_extensions as $extension) {
+        if (preg_match(FILE_INSECURE_EXTENSION_REGEX, 'test.' . $extension)) {
+          // Add the txt extension to the list of allowed extensions if an
+          // insecure is allowed.
+          $allowed_extensions_string .= ' txt';
+          $field->setSetting('file_extensions', $allowed_extensions_string);
+          return TRUE;
+        }
+      }
+      return FALSE;
+    }
+  };
+  \Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'field_config', $updater);
+}
diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
index 202a96682b..c5fb40b215 100644
--- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
@@ -224,14 +224,24 @@ public static function validateDirectory($element, FormStateInterface $form_stat
   public static function validateExtensions($element, FormStateInterface $form_state) {
     if (!empty($element['#value'])) {
       $extensions = preg_replace('/([, ]+\.?)/', ' ', trim(strtolower($element['#value'])));
-      $extensions = array_filter(explode(' ', $extensions));
-      $extensions = implode(' ', array_unique($extensions));
+      $extension_array = array_filter(explode(' ', $extensions));
+      $extensions = implode(' ', array_unique($extension_array));
       if (!preg_match('/^([a-z0-9]+([.][a-z0-9])* ?)+$/', $extensions)) {
         $form_state->setError($element, t('The list of allowed extensions is not valid, be sure to exclude leading dots and to separate extensions with a comma or space.'));
       }
       else {
         $form_state->setValueForElement($element, $extensions);
       }
+
+      // If insecure uploads are not allowed then error if txt is not an allowed
+      // extension.
+      if (!in_array('txt', $extension_array) && !\Drupal::config('system.file')->get('allow_insecure_uploads')) {
+        foreach ($extension_array as $extension) {
+          if (preg_match(FILE_INSECURE_EXTENSION_REGEX, 'test.' . $extension)) {
+            $form_state->setError($element, t('The extension %extension is insecure. In order to allow it to be uploaded allow the "txt" extension.', ['%extension' => $extension]));
+          }
+        }
+      }
     }
   }
 
diff --git a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php
index 8efbb92d5d..a19ab2dbb4 100644
--- a/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php
+++ b/core/modules/file/src/Plugin/rest/resource/FileUploadResource.php
@@ -474,13 +474,13 @@ protected function prepareFilename($filename, array &$validators) {
     // filename.php.txt, respectively). Don't rename if 'allow_insecure_uploads'
     // evaluates to TRUE.
     if (!$this->systemFileConfig->get('allow_insecure_uploads') && preg_match(FILE_INSECURE_EXTENSION_REGEX, $filename) && (substr($filename, -4) != '.txt')) {
-      // The destination filename will also later be used to create the URI.
-      $filename .= '.txt';
-
-      // The .txt extension may not be in the allowed list of extensions. We
-      // have to add it here or else the file upload will fail.
-      if (!empty($validators['file_validate_extensions'][0])) {
-        $validators['file_validate_extensions'][0] .= ' txt';
+      // Rename the file if possible otherwise we have to fail.
+      if (empty($validators['file_validate_extensions'][0]) || preg_match('/(^|\s)txt(\s|$)/', $validators['file_validate_extensions'][0])) {
+        // The destination filename will also later be used to create the URI.
+        $filename .= '.txt';
+      }
+      else {
+        throw new UnprocessableEntityHttpException(sprintf("Unprocessable Entity: The file %s could not be uploaded because the extension is insecure.", $filename));
       }
     }
 
diff --git a/core/modules/file/src/Tests/FileFieldWidgetTest.php b/core/modules/file/src/Tests/FileFieldWidgetTest.php
index 0c18eaf80b..994a318a8f 100644
--- a/core/modules/file/src/Tests/FileFieldWidgetTest.php
+++ b/core/modules/file/src/Tests/FileFieldWidgetTest.php
@@ -336,6 +336,37 @@ public function testPrivateFileSetting() {
     $this->assertFieldByXpath('//input[@id="edit-settings-uri-scheme-public" and not(@disabled)]', 'public', 'Upload destination setting enabled.');
   }
 
+  /**
+   * Tests configuring file field's allowed file extensions setting.
+   */
+  public function testFileExtensionsSetting() {
+    // Grant the admin user required permissions.
+    user_role_grant_permissions($this->adminUser->roles[0]->target_id, ['administer node fields']);
+
+    $type_name = 'article';
+    $field_name = strtolower($this->randomMachineName());
+    $this->createFileField($field_name, 'node', $type_name);
+    $field = FieldConfig::loadByName('node', $type_name, $field_name);
+    $field_id = $field->id();
+
+    // By default allowing PHP files without TXT is not permitted.
+    $edit = ['settings[file_extensions]' => 'jpg php'];
+    $this->drupalPostForm("admin/structure/types/manage/$type_name/fields/$field_id", $edit, t('Save settings'));
+    $this->assertText('The extension php is insecure. In order to allow it to be uploaded allow the "txt" extension.');
+
+    // Test allowing PHP and TXT.
+    $edit = ['settings[file_extensions]' => 'jpg php txt'];
+    $this->drupalPostForm("admin/structure/types/manage/$type_name/fields/$field_id", $edit, t('Save settings'));
+    $this->assertText('Saved ' . $field_name . ' configuration.');
+
+    // If the system is configured to allow insecure uploads, TXT is not
+    // required when allowing PHP.
+    $this->config('system.file')->set('allow_insecure_uploads', TRUE)->save();
+    $edit = ['settings[file_extensions]' => 'jpg php'];
+    $this->drupalPostForm("admin/structure/types/manage/$type_name/fields/$field_id", $edit, t('Save settings'));
+    $this->assertText('Saved ' . $field_name . ' configuration.');
+  }
+
   /**
    * Tests that download restrictions on private files work on comments.
    */
diff --git a/core/modules/file/tests/src/Functional/SaveUploadFormTest.php b/core/modules/file/tests/src/Functional/SaveUploadFormTest.php
index a9b3aca204..0d6e845474 100644
--- a/core/modules/file/tests/src/Functional/SaveUploadFormTest.php
+++ b/core/modules/file/tests/src/Functional/SaveUploadFormTest.php
@@ -213,7 +213,7 @@ public function testHandleDangerousFile() {
       'file_test_replace' => FILE_EXISTS_REPLACE,
       'files[file_test_upload][]' => $file_system->realpath($this->phpfile->uri),
       'is_image_file' => FALSE,
-      'extensions' => 'php',
+      'extensions' => 'php txt',
     ];
 
     $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit'));
@@ -243,6 +243,24 @@ public function testHandleDangerousFile() {
 
     // Turn off insecure uploads.
     $config->set('allow_insecure_uploads', 0)->save();
+
+    // Reset the hook counters.
+    file_test_reset();
+
+    $edit = [
+      'file_test_replace' => FILE_EXISTS_REPLACE,
+      'files[file_test_upload][]' => $file_system->realpath($this->phpfile->uri),
+      'is_image_file' => FALSE,
+      'extensions' => 'php',
+    ];
+
+    $this->drupalPostForm('file-test/save_upload_from_form_test', $edit, t('Submit'));
+    $this->assertResponse(200, 'Received a 200 response for posted test file.');
+    $this->assertSession()->pageTextContains('The file php-2.php could not be uploaded because the extension is insecure.');
+    $this->assertSession()->pageTextContains('Epic upload FAIL!');
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled([]);
   }
 
   /**
diff --git a/core/modules/file/tests/src/Functional/SaveUploadTest.php b/core/modules/file/tests/src/Functional/SaveUploadTest.php
index ea1ac109f1..2662d4c487 100644
--- a/core/modules/file/tests/src/Functional/SaveUploadTest.php
+++ b/core/modules/file/tests/src/Functional/SaveUploadTest.php
@@ -200,7 +200,7 @@ public function testHandleDangerousFile() {
       'file_test_replace' => FILE_EXISTS_REPLACE,
       'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->phpfile->uri),
       'is_image_file' => FALSE,
-      'extensions' => 'php',
+      'extensions' => 'php txt',
     ];
 
     $this->drupalPostForm('file-test/upload', $edit, t('Submit'));
@@ -231,6 +231,24 @@ public function testHandleDangerousFile() {
 
     // Turn off insecure uploads.
     $config->set('allow_insecure_uploads', 0)->save();
+
+    // Reset the hook counters.
+    file_test_reset();
+
+    $edit = [
+      'file_test_replace' => FILE_EXISTS_REPLACE,
+      'files[file_test_upload]' => \Drupal::service('file_system')->realpath($this->phpfile->uri),
+      'is_image_file' => FALSE,
+      'extensions' => 'php',
+    ];
+
+    $this->drupalPostForm('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, 'Received a 200 response for posted test file.');
+    $this->assertSession()->pageTextContains('The file php-2.php could not be uploaded because the extension is insecure.');
+    $this->assertSession()->pageTextContains('Epic upload FAIL!');
+
+    // Check that the correct hooks were called.
+    $this->assertFileHooksCalled([]);
   }
 
   /**
diff --git a/core/modules/file/tests/src/Functional/Update/FileFieldFileExtensionsUpdateTest.php b/core/modules/file/tests/src/Functional/Update/FileFieldFileExtensionsUpdateTest.php
new file mode 100644
index 0000000000..7da1d74b63
--- /dev/null
+++ b/core/modules/file/tests/src/Functional/Update/FileFieldFileExtensionsUpdateTest.php
@@ -0,0 +1,110 @@
+<?php
+
+namespace Drupal\Tests\file\Functional\Update;
+
+use Drupal\Core\Database\Database;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * Tests file_post_update_update_allowed_file_extensions_if_insecure().
+ *
+ * @group Update
+ * @group legacy
+ */
+class FileFieldFileExtensionsUpdateTest extends UpdatePathTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['file'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-8.4.0.bare.standard.php.gz',
+    ];
+  }
+
+  /**
+   * Tests adding txt extension to field that allow insecure extensions.
+   */
+  public function testInsecureUpdatesNotAllowed() {
+    $this->setAllowedExtensions('php jpg');
+    $this->runUpdates();
+    $this->assertResponse('200');
+    $field = FieldConfig::load('node.article.field_image');
+    $this->assertSame('php jpg txt', $field->getSetting('file_extensions'));
+  }
+
+  /**
+   * Tests file fields that permit all extensions.
+   */
+  public function testAllFileTypesAllowed() {
+    $this->setAllowedExtensions('');
+    $this->runUpdates();
+    $this->assertResponse('200');
+    $field = FieldConfig::load('node.article.field_image');
+    $this->assertSame('', $field->getSetting('file_extensions'));
+  }
+
+  /**
+   * Tests update when insecure uploads are allowed.
+   */
+  public function testInsecureUpdatesAllowed() {
+    $this->setAllowedExtensions('php');
+
+    $connection = Database::getConnection();
+    $config = $connection->select('config')
+      ->fields('config', ['data'])
+      ->condition('collection', '')
+      ->condition('name', 'system.file')
+      ->execute()
+      ->fetchField();
+    $config = unserialize($config);
+    $config['allow_insecure_uploads'] = TRUE;
+    $connection->update('config')
+      ->fields([
+        'data' => serialize($config),
+      ])
+      ->condition('collection', '')
+      ->condition('name', 'system.file')
+      ->execute();
+
+    $this->runUpdates();
+    $this->assertSession()->pageTextContains('The system is configured to allow insecure file uploads. No file field updates are necessary.');
+    $this->assertResponse('200');
+    $field = FieldConfig::load('node.article.field_image');
+    $this->assertSame('php', $field->getSetting('file_extensions'));
+  }
+
+  /**
+   * Sets the allowed extensions on the article image field.
+   *
+   * @param $allowed_extensions
+   */
+  protected function setAllowedExtensions($allowed_extensions) {
+    // Do direct database updates to avoid dependencies.
+    $connection = Database::getConnection();
+
+    $config = $connection->select('config')
+      ->fields('config', ['data'])
+      ->condition('collection', '')
+      ->condition('name', 'field.field.node.article.field_image')
+      ->execute()
+      ->fetchField();
+    $config = unserialize($config);
+    $this->assertArrayHasKey('file_extensions', $config['settings']);
+    $config['settings']['file_extensions'] = $allowed_extensions;
+    $connection->update('config')
+      ->fields([
+        'data' => serialize($config),
+      ])
+      ->condition('collection', '')
+      ->condition('name', 'field.field.node.article.field_image')
+      ->execute();
+  }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php b/core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php
index 994a4c3ccb..a5aeef3a51 100644
--- a/core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/FileUploadResourceTestBase.php
@@ -482,9 +482,9 @@ public function testFileUploadMaliciousExtension() {
     $this->assertTrue(file_exists('public://foobar/example.php.txt'));
 
     // Add php as an allowed format. Allow insecure uploads still being FALSE
-    // should still not allow this. So it should still have a .txt extension
-    // appended even though it is not in the list of allowed extensions.
-    $this->field->setSetting('file_extensions', 'php')
+    // should still not allow this. So it should be renamed to have .txt
+    // extension.
+    $this->field->setSetting('file_extensions', 'php txt')
       ->save();
     $this->refreshTestStateAfterRestConfigChange();
 
@@ -496,6 +496,19 @@ public function testFileUploadMaliciousExtension() {
     $this->assertTrue(file_exists('public://foobar/example_2.php.txt'));
     $this->assertFalse(file_exists('public://foobar/example_2.php'));
 
+    // Add php as an allowed format without txt. Allow insecure uploads still
+    // being FALSE should not allow this.
+    $this->field->setSetting('file_extensions', 'php')
+      ->save();
+    $this->refreshTestStateAfterRestConfigChange();
+
+    $response = $this->fileRequest($uri, $php_string, ['Content-Disposition' => 'filename="example_5.php"']);
+    $this->assertResourceErrorResponse(422, "Unprocessable Entity: The file example_5.php could not be uploaded because the extension is insecure.", $response);
+
+    // Make sure that no file was saved.
+    $this->assertFalse(file_exists('public://foobar/example_5.php'));
+    $this->assertFalse(file_exists('public://foobar/example_5.php.txt'));
+
     // Allow .doc file uploads and ensure even a mis-configured apache will not
     // fallback to php because the filename will be munged.
     $this->field->setSetting('file_extensions', 'doc')->save();
