diff --git a/composer.lock b/composer.lock
index a563f65a18d1209e0b2b5f847756ad4c04ccc868..fe02a95636dc12c68b656a59813eb1e2c55cbb03 100644
--- a/composer.lock
+++ b/composer.lock
@@ -2538,6 +2538,46 @@
             ],
             "time": "2018-07-13T07:12:17+00:00"
         },
+        {
+            "name": "typo3/phar-stream-wrapper",
+            "version": "v2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/TYPO3/phar-stream-wrapper.git",
+                "reference": "0469d9fefa0146ea4299d3b11cfbb76faa7045bf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/TYPO3/phar-stream-wrapper/zipball/0469d9fefa0146ea4299d3b11cfbb76faa7045bf",
+                "reference": "0469d9fefa0146ea4299d3b11cfbb76faa7045bf",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3|^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.36"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "TYPO3\\PharStreamWrapper\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Interceptors for PHP's native phar:// stream handling",
+            "homepage": "https://typo3.org/",
+            "keywords": [
+                "phar",
+                "php",
+                "security",
+                "stream-wrapper"
+            ],
+            "time": "2018-10-18T08:46:28+00:00"
+        },
         {
             "name": "wikimedia/composer-merge-plugin",
             "version": "v1.4.1",
diff --git a/core/composer.json b/core/composer.json
index 37120ae8ceecf4ebb912c65bbf5c1b2af662c21b..34fed39b1ae5d60bc43e5c7eee9274f9c516b1d5 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -31,6 +31,7 @@
         "symfony/process": "~3.4.0",
         "symfony/polyfill-iconv": "^1.0",
         "symfony/yaml": "~3.4.5",
+        "typo3/phar-stream-wrapper": "^2.0.1",
         "twig/twig": "^1.35.0",
         "doctrine/common": "^2.5",
         "doctrine/annotations": "^1.2",
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index a099327c99ad5654f854ffc6e996dc2bf4bd83a0..3b14d4d447d431ed1fb759444a3aebba44bc60e7 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -19,6 +19,7 @@
 use Drupal\Core\Http\TrustedHostsRequestFactory;
 use Drupal\Core\Installer\InstallerRedirectTrait;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Security\PharExtensionInterceptor;
 use Drupal\Core\Security\RequestSanitizer;
 use Drupal\Core\Site\Settings;
 use Drupal\Core\Test\TestDatabase;
@@ -35,6 +36,9 @@
 use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
 use Symfony\Component\HttpKernel\TerminableInterface;
 use Symfony\Component\Routing\Route;
+use TYPO3\PharStreamWrapper\Manager as PharStreamWrapperManager;
+use TYPO3\PharStreamWrapper\Behavior as PharStreamWrapperBehavior;
+use TYPO3\PharStreamWrapper\PharStreamWrapper;
 
 /**
  * The DrupalKernel class is the core of Drupal itself.
@@ -471,6 +475,26 @@ public function boot() {
     // Initialize the container.
     $this->initializeContainer();
 
+    if (in_array('phar', stream_get_wrappers(), TRUE)) {
+      // Set up a stream wrapper to handle insecurities due to PHP's builtin
+      // phar stream wrapper. This is not registered as a regular stream wrapper
+      // to prevent \Drupal\Core\File\FileSystem::validScheme() treating "phar"
+      // as a valid scheme.
+      try {
+        $behavior = new PharStreamWrapperBehavior();
+        PharStreamWrapperManager::initialize(
+          $behavior->withAssertion(new PharExtensionInterceptor())
+        );
+      }
+      catch (\LogicException $e) {
+        // Continue if the PharStreamWrapperManager is already initialized. For
+        // example, this occurs during a module install.
+        // @see \Drupal\Core\Extension\ModuleInstaller::install()
+      };
+      stream_wrapper_unregister('phar');
+      stream_wrapper_register('phar', PharStreamWrapper::class);
+    }
+
     $this->booted = TRUE;
 
     return $this;
diff --git a/core/lib/Drupal/Core/Security/PharExtensionInterceptor.php b/core/lib/Drupal/Core/Security/PharExtensionInterceptor.php
new file mode 100644
index 0000000000000000000000000000000000000000..a77e9f84c267cfa2b180db38792b989a80580a23
--- /dev/null
+++ b/core/lib/Drupal/Core/Security/PharExtensionInterceptor.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Drupal\Core\Security;
+
+use TYPO3\PharStreamWrapper\Assertable;
+use TYPO3\PharStreamWrapper\Helper;
+use TYPO3\PharStreamWrapper\Exception;
+
+/**
+ * An alternate PharExtensionInterceptor to support phar-based CLI tools.
+ *
+ * @see \TYPO3\PharStreamWrapper\Interceptor\PharExtensionInterceptor
+ */
+class PharExtensionInterceptor implements Assertable {
+
+  /**
+   * Determines whether phar file is allowed to execute.
+   *
+   * The phar file is allowed to execute if:
+   * - the base file name has a ".phar" suffix.
+   * - it is the CLI tool that has invoked the interceptor.
+   *
+   * @param string $path
+   *   The path of the phar file to check.
+   *
+   * @param string $command
+   *   The command being carried out.
+   *
+   * @return bool
+   *   TRUE if the phar file is allowed to execute.
+   *
+   * @throws Exception
+   *   Thrown when the file is not allowed to execute.
+   */
+  public function assert($path, $command) {
+    if ($this->baseFileContainsPharExtension($path)) {
+      return TRUE;
+    }
+    throw new Exception(
+      sprintf(
+        'Unexpected file extension in "%s"',
+        $path
+      ),
+      1535198703
+    );
+  }
+
+  /**
+   * @param string $path
+   *   The path of the phar file to check.
+   *
+   * @return bool
+   *   TRUE if the file has a .phar extension or if the execution has been
+   *   invoked by the phar file.
+   */
+  private function baseFileContainsPharExtension($path) {
+    $baseFile = Helper::determineBaseFile($path);
+    if ($baseFile === NULL) {
+      return FALSE;
+    }
+    // If the stream wrapper is registered by invoking a phar file that does
+    // not not have .phar extension then this should be allowed. For
+    // example, some CLI tools recommend removing the extension.
+    $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
+    $caller = array_pop($backtrace);
+    if (isset($caller['file']) && $baseFile === $caller['file']) {
+      return TRUE;
+    }
+    $fileExtension = pathinfo($baseFile, PATHINFO_EXTENSION);
+    return strtolower($fileExtension) === 'phar';
+  }
+
+}
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index a6f68094a96025cf423aeb018d577e1223e1ee54..0eb9a64ec7aad0c633fe21009f8dd449340ccbdd 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -23,7 +23,7 @@
 /**
  * The regex pattern used when checking for insecure file types.
  */
-define('FILE_INSECURE_EXTENSION_REGEX', '/\.(php|pl|py|cgi|asp|js)(\.|$)/i');
+define('FILE_INSECURE_EXTENSION_REGEX', '/\.(phar|php|pl|py|cgi|asp|js)(\.|$)/i');
 
 // Load all Field module hooks for File.
 require_once __DIR__ . '/file.field.inc';
diff --git a/core/modules/simpletest/files/phar-1.phar b/core/modules/simpletest/files/phar-1.phar
new file mode 100644
index 0000000000000000000000000000000000000000..8d25e6d4403979bf0fb37b7d10ff721bc804130d
--- /dev/null
+++ b/core/modules/simpletest/files/phar-1.phar
@@ -0,0 +1,301 @@
+<?php
+
+$web = 'index.php';
+
+if (in_array('phar', stream_get_wrappers()) && class_exists('Phar', 0)) {
+Phar::interceptFileFuncs();
+set_include_path('phar://' . __FILE__ . PATH_SEPARATOR . get_include_path());
+Phar::webPhar(null, $web);
+include 'phar://' . __FILE__ . '/' . Extract_Phar::START;
+return;
+}
+
+if (@(isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'POST'))) {
+Extract_Phar::go(true);
+$mimes = array(
+'phps' => 2,
+'c' => 'text/plain',
+'cc' => 'text/plain',
+'cpp' => 'text/plain',
+'c++' => 'text/plain',
+'dtd' => 'text/plain',
+'h' => 'text/plain',
+'log' => 'text/plain',
+'rng' => 'text/plain',
+'txt' => 'text/plain',
+'xsd' => 'text/plain',
+'php' => 1,
+'inc' => 1,
+'avi' => 'video/avi',
+'bmp' => 'image/bmp',
+'css' => 'text/css',
+'gif' => 'image/gif',
+'htm' => 'text/html',
+'html' => 'text/html',
+'htmls' => 'text/html',
+'ico' => 'image/x-ico',
+'jpe' => 'image/jpeg',
+'jpg' => 'image/jpeg',
+'jpeg' => 'image/jpeg',
+'js' => 'application/x-javascript',
+'midi' => 'audio/midi',
+'mid' => 'audio/midi',
+'mod' => 'audio/mod',
+'mov' => 'movie/quicktime',
+'mp3' => 'audio/mp3',
+'mpg' => 'video/mpeg',
+'mpeg' => 'video/mpeg',
+'pdf' => 'application/pdf',
+'png' => 'image/png',
+'swf' => 'application/shockwave-flash',
+'tif' => 'image/tiff',
+'tiff' => 'image/tiff',
+'wav' => 'audio/wav',
+'xbm' => 'image/xbm',
+'xml' => 'text/xml',
+);
+
+header("Cache-Control: no-cache, must-revalidate");
+header("Pragma: no-cache");
+
+$basename = basename(__FILE__);
+if (!strpos($_SERVER['REQUEST_URI'], $basename)) {
+chdir(Extract_Phar::$temp);
+include $web;
+return;
+}
+$pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename));
+if (!$pt || $pt == '/') {
+$pt = $web;
+header('HTTP/1.1 301 Moved Permanently');
+header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt);
+exit;
+}
+$a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt);
+if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) {
+header('HTTP/1.0 404 Not Found');
+echo "<html>\n <head>\n  <title>File Not Found<title>\n </head>\n <body>\n  <h1>404 - File Not Found</h1>\n </body>\n</html>";
+exit;
+}
+$b = pathinfo($a);
+if (!isset($b['extension'])) {
+header('Content-Type: text/plain');
+header('Content-Length: ' . filesize($a));
+readfile($a);
+exit;
+}
+if (isset($mimes[$b['extension']])) {
+if ($mimes[$b['extension']] === 1) {
+include $a;
+exit;
+}
+if ($mimes[$b['extension']] === 2) {
+highlight_file($a);
+exit;
+}
+header('Content-Type: ' .$mimes[$b['extension']]);
+header('Content-Length: ' . filesize($a));
+readfile($a);
+exit;
+}
+}
+
+class Extract_Phar
+{
+static $temp;
+static $origdir;
+const GZ = 0x1000;
+const BZ2 = 0x2000;
+const MASK = 0x3000;
+const START = 'index.php';
+const LEN = 6643;
+
+static function go($return = false)
+{
+$fp = fopen(__FILE__, 'rb');
+fseek($fp, self::LEN);
+$L = unpack('V', $a = fread($fp, 4));
+$m = '';
+
+do {
+$read = 8192;
+if ($L[1] - strlen($m) < 8192) {
+$read = $L[1] - strlen($m);
+}
+$last = fread($fp, $read);
+$m .= $last;
+} while (strlen($last) && strlen($m) < $L[1]);
+
+if (strlen($m) < $L[1]) {
+die('ERROR: manifest length read was "' .
+strlen($m) .'" should be "' .
+$L[1] . '"');
+}
+
+$info = self::_unpack($m);
+$f = $info['c'];
+
+if ($f & self::GZ) {
+if (!function_exists('gzinflate')) {
+die('Error: zlib extension is not enabled -' .
+' gzinflate() function needed for zlib-compressed .phars');
+}
+}
+
+if ($f & self::BZ2) {
+if (!function_exists('bzdecompress')) {
+die('Error: bzip2 extension is not enabled -' .
+' bzdecompress() function needed for bz2-compressed .phars');
+}
+}
+
+$temp = self::tmpdir();
+
+if (!$temp || !is_writable($temp)) {
+$sessionpath = session_save_path();
+if (strpos ($sessionpath, ";") !== false)
+$sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1);
+if (!file_exists($sessionpath) || !is_dir($sessionpath)) {
+die('Could not locate temporary directory to extract phar');
+}
+$temp = $sessionpath;
+}
+
+$temp .= '/pharextract/'.basename(__FILE__, '.phar');
+self::$temp = $temp;
+self::$origdir = getcwd();
+@mkdir($temp, 0777, true);
+$temp = realpath($temp);
+
+if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) {
+self::_removeTmpFiles($temp, getcwd());
+@mkdir($temp, 0777, true);
+@file_put_contents($temp . '/' . md5_file(__FILE__), '');
+
+foreach ($info['m'] as $path => $file) {
+$a = !file_exists(dirname($temp . '/' . $path));
+@mkdir(dirname($temp . '/' . $path), 0777, true);
+clearstatcache();
+
+if ($path[strlen($path) - 1] == '/') {
+@mkdir($temp . '/' . $path, 0777);
+} else {
+file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp));
+@chmod($temp . '/' . $path, 0666);
+}
+}
+}
+
+chdir($temp);
+
+if (!$return) {
+include self::START;
+}
+}
+
+static function tmpdir()
+{
+if (strpos(PHP_OS, 'WIN') !== false) {
+if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) {
+return $var;
+}
+if (is_dir('/temp') || mkdir('/temp')) {
+return realpath('/temp');
+}
+return false;
+}
+if ($var = getenv('TMPDIR')) {
+return $var;
+}
+return realpath('/tmp');
+}
+
+static function _unpack($m)
+{
+$info = unpack('V', substr($m, 0, 4));
+ $l = unpack('V', substr($m, 10, 4));
+$m = substr($m, 14 + $l[1]);
+$s = unpack('V', substr($m, 0, 4));
+$o = 0;
+$start = 4 + $s[1];
+$ret['c'] = 0;
+
+for ($i = 0; $i < $info[1]; $i++) {
+ $len = unpack('V', substr($m, $start, 4));
+$start += 4;
+ $savepath = substr($m, $start, $len[1]);
+$start += $len[1];
+   $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24)));
+$ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3]
+& 0xffffffff);
+$ret['m'][$savepath][7] = $o;
+$o += $ret['m'][$savepath][2];
+$start += 24 + $ret['m'][$savepath][5];
+$ret['c'] |= $ret['m'][$savepath][4] & self::MASK;
+}
+return $ret;
+}
+
+static function extractFile($path, $entry, $fp)
+{
+$data = '';
+$c = $entry[2];
+
+while ($c) {
+if ($c < 8192) {
+$data .= @fread($fp, $c);
+$c = 0;
+} else {
+$c -= 8192;
+$data .= @fread($fp, 8192);
+}
+}
+
+if ($entry[4] & self::GZ) {
+$data = gzinflate($data);
+} elseif ($entry[4] & self::BZ2) {
+$data = bzdecompress($data);
+}
+
+if (strlen($data) != $entry[0]) {
+die("Invalid internal .phar file (size error " . strlen($data) . " != " .
+$stat[7] . ")");
+}
+
+if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) {
+die("Invalid internal .phar file (checksum error)");
+}
+
+return $data;
+}
+
+static function _removeTmpFiles($temp, $origdir)
+{
+chdir($temp);
+
+foreach (glob('*') as $f) {
+if (file_exists($f)) {
+is_dir($f) ? @rmdir($f) : @unlink($f);
+if (file_exists($f) && is_dir($f)) {
+self::_removeTmpFiles($f, getcwd());
+}
+}
+}
+
+@rmdir($temp);
+clearstatcache();
+chdir($origdir);
+}
+}
+
+Extract_Phar::go();
+__HALT_COMPILER(); ?>7������������������	���index.phpµ���8!¾[µ���u‰¾¶������<?php
+/**
+ * @file
+ * This test file is used to test Drupal's phar stream wrapper functionality.
+ *
+ * @see \Drupal\KernelTests\Core\File\PharWrapperTest
+ */
+
+echo 'Hello, world!';
+å1qV«5õ['áyß	R£CØA���GBMB
\ No newline at end of file
diff --git a/core/tests/Drupal/KernelTests/Core/File/PharWrapperTest.php b/core/tests/Drupal/KernelTests/Core/File/PharWrapperTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..2cd2ab1ea94944b3cef1a759369f1df311d6eb0a
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/File/PharWrapperTest.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace Drupal\KernelTests\Core\File;
+
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Tests that the phar stream wrapper works.
+ *
+ * @group File
+ */
+class PharWrapperTest extends KernelTestBase {
+
+  /**
+   * Tests that only valid phar files can be used.
+   */
+  public function testPharFile() {
+    $base = $this->getDrupalRoot() . '/core/modules/simpletest/files';
+    // Ensure that file operations via the phar:// stream wrapper work for phar
+    // files with the .phar extension.
+    $this->assertFalse(file_exists("phar://$base/phar-1.phar/no-such-file.php"));
+    $this->assertTrue(file_exists("phar://$base/phar-1.phar/index.php"));
+    $file_contents = file_get_contents("phar://$base/phar-1.phar/index.php");
+    $expected_hash = 'c7e7904ea573c5ebea3ef00bb08c1f86af1a45961fbfbeb1892ff4a98fd73ad5';
+    $this->assertSame($expected_hash, hash('sha256', $file_contents));
+
+    // Ensure that file operations via the phar:// stream wrapper throw an
+    // exception for files without the .phar extension.
+    $this->setExpectedException('TYPO3\PharStreamWrapper\Exception');
+    file_exists("phar://$base/image-2.jpg/index.php");
+  }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/File/StreamWrapperTest.php b/core/tests/Drupal/KernelTests/Core/File/StreamWrapperTest.php
index 6c18b6654ccda69848b8851ae01feb81091fe681..070782fcba67537a178638197cafee33525dc9fc 100644
--- a/core/tests/Drupal/KernelTests/Core/File/StreamWrapperTest.php
+++ b/core/tests/Drupal/KernelTests/Core/File/StreamWrapperTest.php
@@ -144,4 +144,31 @@ public function testGetValidStreamScheme() {
     $this->assertFalse(file_stream_wrapper_valid_scheme(file_uri_scheme('foo://asdf')), 'Did not get a valid stream scheme from foo://asdf');
   }
 
+  /**
+   * Tests that phar stream wrapper is registered as expected.
+   *
+   * @see \Drupal\Core\StreamWrapper\StreamWrapperManager::register()
+   */
+  public function testPharStreamWrapperRegistration() {
+    if (!in_array('phar', stream_get_wrappers(), TRUE)) {
+      $this->markTestSkipped('There is no phar stream wrapper registered. PHP is probably compiled without phar support.');
+    }
+    // Ensure that phar is not treated as a valid scheme.
+    $stream_wrapper_manager = $this->container->get('stream_wrapper_manager');
+    $this->assertFalse($stream_wrapper_manager->getViaScheme('phar'));
+
+    // Ensure that calling register again and unregister do not create errors
+    // due to the PharStreamWrapperManager singleton.
+    $stream_wrapper_manager->register();
+    $this->assertContains('public', stream_get_wrappers());
+    $this->assertContains('phar', stream_get_wrappers());
+    $stream_wrapper_manager->unregister();
+    $this->assertNotContains('public', stream_get_wrappers());
+    // This will have reverted to the builtin phar stream wrapper.
+    $this->assertContains('phar', stream_get_wrappers());
+    $stream_wrapper_manager->register();
+    $this->assertContains('public', stream_get_wrappers());
+    $this->assertContains('phar', stream_get_wrappers());
+  }
+
 }