diff --git a/core/includes/file.inc b/core/includes/file.inc
index baaf654..75490cb 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -149,6 +149,16 @@
 const FILE_STATUS_PERMANENT = 1;
 
 /**
+ * Allow only the listed file extensions.
+ */
+const FILE_ALLOW_EXTENSIONS_LISTED = 0;
+
+/**
+ * Allow all extensions except those listed.
+ */
+const FILE_ALLOW_EXTENSIONS_NOTLISTED = 1;
+
+/**
  * Provides Drupal stream wrapper registry.
  *
  * A stream wrapper is an abstraction of a file system that allows Drupal to
@@ -851,7 +861,7 @@ function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXIST
  *
  * Specifically, this function adds an underscore to all extensions that are
  * between 2 and 5 characters in length, internal to the file name, and not
- * included in $extensions.
+ * allowed by $extensions.
  *
  * Function behavior is also controlled by the configuration
  * 'system.file:allow_insecure_uploads'. If it evaluates to TRUE, no alterations
@@ -860,6 +870,9 @@ function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXIST
  *   File name to modify.
  * @param $extensions
  *   A space-separated list of extensions that should not be altered.
+ * @param $inclusion
+ *   If FILE_ALLOW_EXTENSIONS_NOTLISTED, the list of extensions is considered
+ *   a blacklist. Otherwise, it is considered a whitelist.
  * @param $alerts
  *   If TRUE, drupal_set_message() will be called to display a message if the
  *   file name was changed.
@@ -867,7 +880,7 @@ function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXIST
  * @return string
  *   The potentially modified $filename.
  */
-function file_munge_filename($filename, $extensions, $alerts = TRUE) {
+function file_munge_filename($filename, $extensions, $alerts = TRUE, $inclusion = FILE_ALLOW_EXTENSIONS_LISTED) {
   $original = $filename;
 
   // Allow potentially insecure uploads for very savvy users and admin
@@ -875,7 +888,7 @@ function file_munge_filename($filename, $extensions, $alerts = TRUE) {
     // Remove any null bytes. See http://php.net/manual/en/security.filesystem.nullbytes.php
     $filename = str_replace(chr(0), '', $filename);
 
-    $whitelist = array_unique(explode(' ', trim($extensions)));
+    $extensions_arr = array_unique(explode(' ', trim($extensions)));
 
     // Split the filename up by periods. The first part becomes the basename
     // the last part the final extension.
@@ -884,11 +897,14 @@ function file_munge_filename($filename, $extensions, $alerts = TRUE) {
     $final_extension = array_pop($filename_parts); // Remove final extension.
 
     // Loop through the middle parts of the name and add an underscore to the
-    // end of each section that could be a file extension but isn't in the list
-    // of allowed extensions.
+    // end of each section that could be a file extension but is restricted by
+    // the given extension list
     foreach ($filename_parts as $filename_part) {
       $new_filename .= '.' . $filename_part;
-      if (!in_array($filename_part, $whitelist) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) {
+      if ($inclusion == FILE_ALLOW_EXTENSIONS_NOTLISTED && in_array($filename_part, $extensions_arr) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) {
+        $new_filename .= '_';
+      }
+      elseif (!in_array($filename_part, $extensions_arr) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) {
         $new_filename .= '_';
       }
     }
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index a3c7140..490d99b 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -196,9 +196,15 @@ function theme_file_upload_help($variables) {
   if (isset($upload_validators['file_validate_size'])) {
     $descriptions[] = t('!size limit.', array('!size' => format_size($upload_validators['file_validate_size'][0])));
   }
-  if (isset($upload_validators['file_validate_extensions'])) {
-    $descriptions[] = t('Allowed types: !extensions.', array('!extensions' => check_plain($upload_validators['file_validate_extensions'][0])));
-  }
+  if (isset($upload_validators['file_validate_extensions']) && !empty($upload_validators['file_validate_extensions'][0])) {
+    if ($upload_validators['file_validate_extensions'][1] == FILE_ALLOW_EXTENSIONS_NOTLISTED) {
+      $inclusion = t('Disallowed');
+    }
+    else {
+      $inclusion = t('Allowed');
+    }
+    $descriptions[] = t('!inclusion file types: !extensions.', array('!inclusion' => $inclusion, '!extensions' => '<strong>' . check_plain($upload_validators['file_validate_extensions'][0]) . '</strong>'));
+   }
 
   if (isset($upload_validators['file_validate_image_resolution'])) {
     $max = $upload_validators['file_validate_image_resolution'][0];
diff --git a/core/modules/file/file.js b/core/modules/file/file.js
index d10f6e1..a81af20 100644
--- a/core/modules/file/file.js
+++ b/core/modules/file/file.js
@@ -11,6 +11,9 @@
 
   "use strict";
 
+  var fileAllowExtensionsListed = 0;
+  var fileAllowExtensionsNotListed = 1;
+
   /**
    * Attach behaviors to managed file element upload fields.
    */
@@ -20,9 +23,17 @@
       var elements;
 
       function initFileValidation (selector) {
+
         $context.find(selector)
           .once('fileValidate')
           .on('change.fileValidate', { extensions: elements[selector] }, Drupal.file.validateExtension);
+
+        /*
+        var extensions = elements[selector][0];
+        var inclusion = elements[selector][1] || fileAllowExtensionsListed;
+        $(selector, context).bind('change', {extensions: extensions, inclusion: inclusion}, Drupal.file.validateExtension);
+        */
+
       }
 
       if (settings.file && settings.file.elements) {
@@ -98,14 +109,19 @@
      */
     validateExtension: function (event) {
       event.preventDefault();
+      // Avoid looping when the value is cleared.
+      if (this.value == '') {
+        return true;
+      }
       // Remove any previous errors.
       $('.file-upload-js-error').remove();
 
       // Add client side validation for the input[type=file].
+      var inclusion = event.data.inclusion;
       var extensionPattern = event.data.extensions.replace(/,\s*/g, '|');
       if (extensionPattern.length > 1 && this.value.length > 0) {
         var acceptableMatch = new RegExp('\\.(' + extensionPattern + ')$', 'gi');
-        if (!acceptableMatch.test(this.value)) {
+        if (inclusion == fileAllowExtensionsListed && !acceptableMatch.test(this.value)) {
           var error = Drupal.t("The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.", {
             // According to the specifications of HTML5, a file upload control
             // should not reveal the real local path to the file that a user
@@ -122,6 +138,15 @@
           // Cancel all other change event handlers.
           event.stopImmediatePropagation();
         }
+        else if (inclusion == fileAllowExtensionsNotListed && acceptableMatch.test(this.value)) {
+          var error = Drupal.t("The selected file %filename cannot be uploaded. Files with the following extensions are not allowed: %extensions.", {
+            '%filename': this.value,
+            '%extensions': extensionPattern.replace(/\|/g, ', ')
+          });
+          $(this).closest('div.form-managed-file').prepend('<div class="messages error file-upload-js-error">' + error + '</div>');
+          this.value = '';
+          return false;
+        }
       }
     },
     /**
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 67e5452..27fbb8c 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -346,13 +346,16 @@ function file_validate_name_length(File $file) {
  *
  * @see hook_file_validate()
  */
-function file_validate_extensions(File $file, $extensions) {
+function file_validate_extensions(File $file, $extensions, $inclusion = FILE_ALLOW_EXTENSIONS_LISTED) {
   $errors = array();
 
   $regex = '/\.(' . preg_replace('/ +/', '|', preg_quote($extensions)) . ')$/i';
-  if (!preg_match($regex, $file->getFilename())) {
+  if ($inclusion == FILE_ALLOW_EXTENSIONS_LISTED && !preg_match($regex, $file->filename)) {
     $errors[] = t('Only files with the following extensions are allowed: %files-allowed.', array('%files-allowed' => $extensions));
   }
+  elseif ($inclusion == FILE_ALLOW_EXTENSIONS_NOTLISTED && preg_match($regex, $file->filename))  {
+    $errors[] = t('Files with the following extensions are not allowed: %files-allowed.', array('%files-allowed' => $extensions));
+  }
   return $errors;
 }
 
@@ -862,13 +865,22 @@ function file_save_upload($form_field_name, array &$form_state, $validators = ar
     if (!\Drupal::config('system.file')->get('allow_insecure_uploads') && preg_match('/\.(php|pl|py|cgi|asp|js)(\.|$)/i', $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');
+      $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';
-        drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->getFilename())));
+        if ($validators['file_validate_extensions'][1] == FILE_ALLOW_EXTENSIONS_NOTLISTED) {
+          $validators['file_validate_extensions'][0] = preg_replace('/(^txt\b\s?|\b\s?txt\b)/i', '', $validators['file_validate_extensions'][0]);
+          if (!strlen($validators['file_validate_extensions'][0])) {
+            // The only extension was removed, so remove the validator
+            unset($validators['file_validate_extensions']);
+          }
+        }
+        else {
+          $validators['file_validate_extensions'][0] .= ' txt';
+        }
       }
+      drupal_set_message(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $file->filename)));
     }
 
     // If the destination is not provided, use the temporary directory.
@@ -1261,12 +1273,24 @@ function file_managed_file_process($element, &$form_state, $form) {
   }
 
   // Add the extension list to the page as JavaScript settings.
-  if (isset($element['#upload_validators']['file_validate_extensions'][0])) {
-    $extension_list = implode(',', array_filter(explode(' ', $element['#upload_validators']['file_validate_extensions'][0])));
+  if (isset($element['#upload_validators']['file_validate_extensions'])) {
+    $extension_list = '';
+    if (!empty($element['#upload_validators']['file_validate_extensions'])) {
+      $extension_list = implode(',', array_filter(explode(' ', $element['#upload_validators']['file_validate_extensions'][0])));
+    }
     $element['upload']['#attached']['js'] = array(
       array(
         'type' => 'setting',
-        'data' => array('file' => array('elements' => array('#' . $element['#id'] => $extension_list)))
+        'data' => array(
+          'file' => array(
+            'elements' => array(
+              '#' . $element['#id'] . '-upload' => array(
+                $extension_list,
+                $element['#upload_validators']['file_validate_extensions'][1]
+              )
+            )
+          )
+        )
       )
     );
   }
diff --git a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php
index d72ccad..acbc6f2 100644
--- a/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/lib/Drupal/file/Plugin/Field/FieldType/FileItem.php
@@ -150,16 +150,21 @@ public function instanceSettingsForm(array $form, array &$form_state) {
 
     // Make the extension list a little more human-friendly by comma-separation.
     $extensions = str_replace(' ', ', ', $settings['file_extensions']);
+    $element['inclusion'] = array(
+      '#type' => 'radios',
+      '#title' => t('Allowed file extensions'),
+      '#options' => array(
+        FILE_ALLOW_EXTENSIONS_LISTED => t('Only the listed extensions'),
+        FILE_ALLOW_EXTENSIONS_NOTLISTED => t('All extensions except those listed'),
+      ),
+      '#default_value' => isset($settings['inclusion']) ? $settings['inclusion'] : FILE_ALLOW_EXTENSIONS_LISTED,
+    );
     $element['file_extensions'] = array(
       '#type' => 'textfield',
-      '#title' => t('Allowed file extensions'),
       '#default_value' => $extensions,
       '#description' => t('Separate extensions with a space or comma and do not include the leading dot.'),
       '#element_validate' => array(array(get_class($this), 'validateExtensions')),
       '#weight' => 1,
-      // By making this field required, we prevent a potential security issue
-      // that would allow files of any type to be uploaded.
-      '#required' => TRUE,
     );
 
     $element['max_filesize'] = array(
@@ -222,6 +227,9 @@ public static function validateExtensions($element, &$form_state) {
         form_set_value($element, $extensions, $form_state);
       }
     }
+    elseif ($form_state['values']['instance']['settings']['inclusion'] == FILE_ALLOW_EXTENSIONS_LISTED) {
+      form_error($element, t('You must provide a list of extensions. If you would like to allow all extensions, leave the list of extensions blank, and change the inclusion settings to "All extensions except those listed".'));
+    }
   }
 
   /**
@@ -271,6 +279,11 @@ public function getUploadValidators() {
     $validators = array();
     $settings = $this->getSettings();
 
+    // by default make inclusion setting as secure as possible
+    if (!isset($settings['inclusion'])) {
+      $settings['inclusion'] = FILE_ALLOW_EXTENSIONS_LISTED;
+    }
+
     // Cap the upload size according to the PHP limit.
     $max_filesize = parse_size(file_upload_max_size());
     if (!empty($settings['max_filesize'])) {
@@ -284,6 +297,9 @@ public function getUploadValidators() {
     if (!empty($settings['file_extensions'])) {
       $validators['file_validate_extensions'] = array($settings['file_extensions']);
     }
+    if (!empty($settings['file_extensions']) || $settings['inclusion'] == FILE_ALLOW_EXTENSIONS_NOTLISTED) {
+      $validators['file_validate_extensions'] = array($settings['file_extensions'], $settings['inclusion']);
+    }
 
     return $validators;
   }
diff --git a/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
index b85b187..9c59fed 100644
--- a/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/FileFieldValidateTest.php
@@ -154,6 +154,24 @@ function testFileExtension() {
     $node_file = file_load($node->{$field_name}->target_id);
     $this->assertFileExists($node_file, 'File exists after uploading a file with extension checking.');
     $this->assertFileEntryExists($node_file, 'File entry exists after uploading a file with extension checking.');
+
+    // Enable extension checking for text files, but exclude it.
+    $this->updateFileField($field_name, $type_name, array('inclusion' => FILE_ALLOW_EXTENSIONS_NOTLISTED, 'file_extensions' => $test_file_extension));
+
+    // Check that the file with the excluded extension cannot be uploaded.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $error_message = t('Files with the following extensions are not allowed: %files-allowed.', array('%files-allowed' => $test_file_extension));
+    $this->assertRaw($error_message, t('Node save failed when file uploaded with the wrong extension.'));
+
+    // Enable extension checking for text and image files.
+    $this->updateFileField($field_name, $type_name, array('inclusion' => FILE_ALLOW_EXTENSIONS_NOTLISTED, 'file_extensions' => "txt"));
+
+    // Check that the file can be uploaded with extension checking.
+    $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
+    $node = node_load($nid, NULL, TRUE);
+    $node_file = (object) $node->{$field_name}[LANGUAGE_NOT_SPECIFIED][0];
+    $this->assertFileExists($node_file, t('File exists after uploading a file with extension checking.'));
+    $this->assertFileEntryExists($node_file, t('File entry exists after uploading a file with extension checking.'));
   }
 
 }
diff --git a/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php b/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php
index 6596f87..188622e 100644
--- a/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php
+++ b/core/modules/file/lib/Drupal/file/Tests/SaveUploadTest.php
@@ -191,7 +191,7 @@ function testHandleDangerousFile() {
 
     $this->drupalPostForm('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, 'Received a 200 response for posted test file.');
-    $message = t('For security reasons, your upload has been renamed to') . ' <em class="placeholder">' . $this->phpfile->filename . '.txt' . '</em>';
+    $message = t('For security reasons, your upload has been renamed to') . ' <em class="placeholder">' . $this->phpfile->filename . '_.txt' . '</em>';
     $this->assertRaw($message, 'Dangerous file was renamed.');
     $this->assertRaw(t('File MIME type is text/plain.'), "Dangerous file's MIME type was changed.");
     $this->assertRaw(t('You WIN!'), 'Found the success message.');
diff --git a/core/modules/system/lib/Drupal/system/Tests/File/NameMungingTest.php b/core/modules/system/lib/Drupal/system/Tests/File/NameMungingTest.php
index 9c0d3d9..22bbd3e 100644
--- a/core/modules/system/lib/Drupal/system/Tests/File/NameMungingTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/File/NameMungingTest.php
@@ -31,7 +31,7 @@ function setUp() {
   function testMunging() {
     // Disable insecure uploads.
     \Drupal::config('system.file')->set('allow_insecure_uploads', 0)->save();
-    $munged_name = file_munge_filename($this->name, '', TRUE);
+    $munged_name = file_munge_filename($this->name, '');
     $messages = drupal_get_messages();
     $this->assertTrue(in_array(t('For security reasons, your upload has been renamed to %filename.', array('%filename' => $munged_name)), $messages['status']), 'Alert properly set when a file is renamed.');
     $this->assertNotEqual($munged_name, $this->name, format_string('The new filename (%munged) has been modified from the original (%original)', array('%munged' => $munged_name, '%original' => $this->name)));
@@ -69,7 +69,7 @@ function testMungeIgnoreWhitelisted() {
    * Ensure that unmunge gets your name back.
    */
   function testUnMunge() {
-    $munged_name = file_munge_filename($this->name, '', FALSE);
+    $munged_name = file_munge_filename($this->name, '', FALSE, FILE_ALLOW_EXTENSIONS_LISTED);
     $unmunged_name = file_unmunge_filename($munged_name);
     $this->assertIdentical($unmunged_name, $this->name, format_string('The unmunged (%unmunged) filename matches the original (%original)', array('%unmunged' => $unmunged_name, '%original' => $this->name)));
   }
